c++ - 使用 AVX 指令实现 _mm256_permutevar8x32_ps

标签 c++ sse simd avx

AVX2 内在 _mm256_permutevar8x32_ps 可以跨 channel 执行洗牌,这对于长度为 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”后缀),因此不适合这种情况。

提前致谢。

最佳答案

要在 AVX 中跨 channel 置换,您可以在 channel 内置换,然后使用 _mm256_permute2f128_ps 交换 channel ,然后混合。例如。假设您要将数组 {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_ps 也是 has 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 Librarylookup8 函数.

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);
}

关于c++ - 使用 AVX 指令实现 _mm256_permutevar8x32_ps,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24323246/

相关文章:

c++ - 如何定义operator<<方法的结束?

c++ - 更改编辑控件中文本的背景

c++ - 为什么两个连续的收集指令比等效的基本操作执行得更差?

x86 - 字节序如何与SIMD寄存器一起工作?

arm - 使用 NEON 指令进行比较操作

c++ - 确定 linux 发行版的宏

c++ - 标准标题上的 Visual Studio 2017 错误

c - 两个固定长度整数数组的元素之和

c - 英特尔 SSE : Why does `_mm_extract_ps` return `int` instead of `float` ?

simd - 如何使用 NEON SIMD 合并 2 行的元素?