Ste*_*rns 4 x86 g++ intrinsics avx avx2
我想要一个AVX2(或更早)的内在函数,它将一个8宽的32位整数向量(总共256位)转换为8宽16位整数向量(总共128位)[丢弃每个元素的高16位] ].这应该是"_mm256_cvtepi16_epi32"的反转.如果没有直接指令,我应该如何通过一系列指令来做到这一点?
在AVX512F之前没有单指令反转. __m128i _mm256_cvtepi32_epi16(__m256i a)
(VPMOVDW
),也可用于512-> 256或128-> low_half_of_128.(输入小于512位ZMM寄存器的版本也需要AVX512VL,因此只需要Skylake-X,而不是Xeon Phi KNL).
有AVX512指令的有符号/无符号饱和版本,但只有AVX512有一个打包指令(截断每个元素的高字节)而不是饱和.
或者使用AVX512BW,您可以使用vpermi2w
两个512位输入向量生成512位结果来模拟通道交叉2输入包.在Skylake-AVX512上,它解码为多个shuffle uops,但同样如此VPMOVDW
,这也是一个小于dword(32位)的粒度的交叉shuffle. http://instlatx64.atw.hu/有一个SKX uops/ports的电子表格.
SSE2/AVX2包指令_mm256_packus_epi32
(如(vpackusdw
))执行有符号或无符号饱和,以及在每个128位通道内操作.这与车道交叉行为不同vpmovzxwd
.
但是,您可以_mm256_and_si256
在打包之前清除高字节.如果你有多个输入向量,这可能会很好,因为packs_epi32
它需要2个输入向量并产生256位输出.
a = H G F E | D C B A 32-bit signed elements, shown from high element to low element, low 128-bit lane on the right
b = P O N M | L K J I
_mm256_packus_epi32(a, b) 16-bit unsigned elements
P O N M H G F E | L K J I D C B A
elements from first operand go to the low half of each lane
Run Code Online (Sandbox Code Playgroud)
如果你能够有效地使用2x vpand
/ vpackuswd ymm
/ vpermq ymm
来获得256位向量,并且所有元素的顺序正确,那么这在Intel CPU上可能是最好的.每256位结果只有2个shuffle uops(4个uop),你可以在一个向量中得到它们.
或者,您可以使用SSSE3/AVX2 vpshufb
(_mm256_shuffle_epi8
)从单个输入中提取所需的字节,并将每个128位通道的另一半归零(通过设置该元素的shuffle-control值以设置符号位).然后使用AVX2 vpermq
将来自两个通道的数据混洗到低128.
__m256i trunc_elements = _mm256_shuffle_epi8(res256, shuffle_mask_32_to_16);
__m256i ordered = _mm256_permute4x64_epi64(trunc_elements, 0x58);
__m128i result = _mm256_castsi256_si128(ordered); // no asm instructions
Run Code Online (Sandbox Code Playgroud)
因此,每128位结果为2 uop,但两个uop都是仅在支持AVX2的主流Intel CPU上的端口5上运行的shuffle.这很好,作为循环的一部分,做了大量的工作,可以保持port0/port1忙,或者无论如何你需要单独的每个128位块.
对于Ryzen/Excavator来说,车道交叉vpermq
是昂贵的(因为他们将256位指令分成多个128位uop,并且没有真正的车道交叉shuffle单元:http: //agner.org/optimize/).所以你想vextracti128
/ vpor
要结合.或者也许vpunpcklqdq
你可以加载相同的shuffle mask set1_epi64
而不需要一个完整的256位向量常量来将上层通道中的元素混洗到该通道的高64位.