使用AVX指令实现_mm256_permutevar8x32_ps

时间:2014-06-20 08:42:22

标签: c++ sse simd avx

AVX2内在_mm256_permutevar8x32_ps可以在通道上执行混洗,这对于排序长度为8的数组非常有用。

现在我只有AVX(Ivy Bridge)并希望在最小周期内做同样的事情。请注意,数据和索引在编译时都是输入和未知的。

例如,数组为[1,2,3,4,5,6,7,8],索引为[3,0,1,7,6,5,2,4],输出应为[4,1,2,8,7,6,3,5]

大多数方便的内在函数的控制掩码必须是常量(没有“var”后缀),因此在这种情况下不适合。

提前致谢。

1 个答案:

答案 0 :(得分:4)

要在AVX中跨越车道进行置换,您可以在车道内进行置换,然后使用_mm256_permute2f128_ps来交换车道,然后进行混合。例如。我们假设您要将数组{1,2,3,4,5,6,7,8}更改为{0,0,1,2,3,4,5,6}。你可以这样做

__m256 t0 = _mm256_permute_ps(x, _MM_SHUFFLE(1, 0, 3, 2));
__m256 t1 = _mm256_permute2f128_ps(t0, t0, 41);
__m256 y = _mm256_blend_ps(t0, t1, 0x33);

_mm256_permute2f128_pshas a zeroing feature这可能非常有用(另请参阅Intel Intrinsics Guide Online)。我在上面的代码中使用它来将第一个通道交换到第二个通道然后将第一个通道归零。看到 shifting-sse-avx-registers-32-bits-left-and-right-while-shifting-in-zeros了解更多详情。

编辑:permutevar内在函数允许运行时置换,因此不限于编译时常量。以下代码是Agner Fog's Vector Class Library中的lookup8函数。

static inline Vec8f lookup8(Vec8i const & index, Vec8f const & table) {
#if INSTRSET >= 8 && VECTORI256_H > 1 // AVX2
#if defined (_MSC_VER) && _MSC_VER < 1700 && ! defined(__INTEL_COMPILER)        
    // bug in MS VS 11 beta: operands in wrong order. fixed in 11.0
    return _mm256_permutevar8x32_ps(_mm256_castsi256_ps(index), _mm256_castps_si256(table)); 
#elif defined (GCC_VERSION) && GCC_VERSION <= 40700 && !defined(__INTEL_COMPILER) && !defined(__clang__)
        // Gcc 4.7.0 has wrong parameter type and operands in wrong order. fixed in version 4.7.1
    return _mm256_permutevar8x32_ps(_mm256_castsi256_ps(index), table);
#else
    // no bug version
    return _mm256_permutevar8x32_ps(table, index);
#endif

#else // AVX
    // swap low and high part of table
    __m256  t1 = _mm256_castps128_ps256(_mm256_extractf128_ps(table, 1));
    __m256  t2 = _mm256_insertf128_ps(t1, _mm256_castps256_ps128(table), 1);
    // join index parts
    __m256i index2 = _mm256_insertf128_si256(_mm256_castsi128_si256(index.get_low()), index.get_high(), 1);
    // permute within each 128-bit part
    __m256  r0 = _mm256_permutevar_ps(table, index2);
    __m256  r1 = _mm256_permutevar_ps(t2,    index2);
    // high index bit for blend
    __m128i k1 = _mm_slli_epi32(index.get_high() ^ 4, 29);
    __m128i k0 = _mm_slli_epi32(index.get_low(),      29);
    __m256  kk = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_castsi128_ps(k0)), _mm_castsi128_ps(k1), 1);
    // blend the two permutes
    return _mm256_blendv_ps(r0, r1, kk);
#endif
}

以下是get_lowget_high函数:

Vec2db get_low() const {
    return _mm256_castpd256_pd128(ymm);
}
Vec2db get_high() const {
    return _mm256_extractf128_pd(ymm,1);
}