优化从 AVX2 寄存器中提取 64 位值

Posted

技术标签:

【中文标题】优化从 AVX2 寄存器中提取 64 位值【英文标题】:Optimize extraction of 64 bit value from AVX2 register 【发布时间】:2014-01-13 00:55:18 【问题描述】:

我尝试从 __m256i 寄存器中提取 64 位。 我当前的提取函数示例:

             byte     31                    16 15                    0
byte_result_vec        000D  000C  000B  000A   000H  000G  000F  000E

_mm256_packs_epi32 ->  0D0C  0B0A  0D0C  0B0A   0H0G  0F0E  0H0G  0F0E

_mm256_packus_epi16 -> DCBA  DCBA  DCBA  DCBA   HGFE  HGFE  HGFE  HGFE
                                         ^^^^                     ^^^^
_mm256_castsi256_si128   -> HGFE  HGFE  HGFE  HGFE

_mm256_extracti128_si256 -> DCBA  DCBA  DCBA  DCBA

_mm_cvtsi128_si32(byte_result_vec1) ->  ABCD

_mm_cvtsi128_si32(byte_result_vec2) ->  EFGH

以下代码将 4x8 位移动到寄存器位置 0-3,而不是提取 32 位。

        byte_result_vec = _mm256_packs_epi32(byte_result_vec, byte_result_vec);
        byte_result_vec = _mm256_packus_epi16(byte_result_vec, byte_result_vec);
        __m128i byte_result_vec1 = _mm256_castsi256_si128(byte_result_vec);
        __m128i byte_result_vec2 = _mm256_extracti128_si256(byte_result_vec,1);
        const int res1 = _mm_cvtsi128_si32(byte_result_vec1);
        const int res2 = _mm_cvtsi128_si32(byte_result_vec2);
        result_array[j]       = res1;
        result_array[j+1]     = res2;

代码运行正常,但速度很慢。 看起来将 res1 和 res2 复制到 result_array 需要的时间最多。 有办法优化吗?

【问题讨论】:

【参考方案1】:

可能这个变种会更快

/* byte_result_vec        000H  000G  000F  000E   000D  000C  000B  000A */
const __m256i shuffle_mask = _mm256_setr_epi8(0,  4,  8, 12, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 4, 8, 12, -1, -1, -1, -1, -1, -1, -1, -1);
/* abcdefgh               0000  0000  HGFE  0000   0000  0000  0000  DCBA */
const __m256i abcdefgh = _mm256_shuffle_epi8(byte_result_vec, shuffle_mask);
/* abcd                                            0000  0000  0000  DCBA */
const __m128i abcd = _mm256_castsi256_si128(abcdefgh);
/* efgh                                            0000  0000  HGFE  0000 */
const __m128i efgh = _mm256_extracti128_si256(abcdefgh, 1);
_mm_storel_epi64((__m128i*)&result_array[j], _mm_or_si128(abcd, efgh));

【讨论】:

我刚刚发现还有一个额外的优化机会:可以将_mm256_castsi256_si128 + _mm256_extracti128_si256 + _mm_or_si128 替换为单个_mm256_permutevar8x32_epi32 以实现另一个延迟周期。 你能告诉我_mm256_castsi256_si128_mm256_castsf256_si128 之间的区别吗?第一个需要 AVX2,第二个只需要 AVX。为什么要使用_mm256_castsi256_si128 _mm256_castsi256_si128 是无操作的,只需要AVX。类似地,其他强制转换是无操作的,它们仅在参数/结果类型上有所不同。 对不起,我问错问题了。我的意思是_mm256_extractf128_si256_mm256_extracti128_si256 之间有什么区别。我知道这些强制转换只是为了让编译器高兴。我的意思是***.com/questions/25684454/… 英特尔 CPU 传统上具有用于整数和浮点寄存器的单独寄存器文件和数据路径。因此,VEXTRACTI128 可能在 SIMD 内核的整数部分实现并连接到整数 SIMD 寄存器文件,VEXTRACTF128 - 在浮点部分实现并连接到 FP SIMD 寄存器文件。因此,如果您将 VEXTRACTI128 与浮点运算产生的数据一起使用(因此寄存器驻留在 FP 文件中),则在 SIMD 内核的各个部分之间移动数据可能会有几个周期的延迟。

以上是关于优化从 AVX2 寄存器中提取 64 位值的主要内容,如果未能解决你的问题,请参考以下文章

AVX2:AVX 寄存器中 8 位元素的 CountTrailingZeros

从填充为 0 的数组加载到 256 位 AVX2 寄存器

AVX2 1x mm256i 32bit 到 2x mm256i 64bit

认识寄存器(X64)

认识寄存器(X64)

转置 8x8 64 位矩阵