如何在每32位数据通道中存储低8位,例如,一个zmm寄存器存储16个32位整数,我只需要将低8位数据存储到内存中,内存是int8_t数组?
5anewei61#
有一条指令vpmovdb(https://www.felixcloutier.com/x86/vpmovdb:vpmovsdb:vpmovusdb),但不幸的是,Intel CPU将其作为2个微指令运行,都是针对端口5的。https://uops.info/和https://agner.org/optimize/)。C内部函数
vpmovdb
VPMOVDB __m128i _mm512_cvtepi32_epi8( __m512i a); VPMOVDB __m128i _mm512_mask_cvtepi32_epi8(__m128i s, __mmask16 k, __m512i a); VPMOVDB __m128i _mm512_maskz_cvtepi32_epi8( __mmask16 k, __m512i a); VPMOVDB void _mm512_mask_cvtepi32_storeu_epi8(void * d, __mmask16 k, __m512i a);
有趣的事实:它允许内存 * 目的地 *,并且只需要AVX 512 F,而不是AVX 512 BW,所以这就是KNL至强融核可以进行字节粒度掩码存储的方式。有符号和无符号饱和形式,但您需要截断形式。截断和跨道填充都是AVX-512中的新技术;在AVX 2中,通常必须使用2x vpackssdw来馈送vpackuswb和vpermq修复,还需要在打包之前屏蔽32位数据,使其位于0..255无符号范围内,这将为每个 input 向量额外消耗vpand。
vpackssdw
vpackuswb
vpermq
vpand
如果您有多个数据矢量,使用AVX-512 VBMI(Ice Lake / Zen 4)vpermt2b从两个矢量中获取字节可能是值得的。在支持它的Intel CPU上,这将运行3个uop(2 p5 + p015),因此每个256位矢量2个周期,而不是每个128位矢量2个周期。它在Zen 4上是1 uop,和vpmovdb一样,所以vpermt2b是理想的,每个时钟允许一个256位存储,速度是Intel的两倍,前端uop更少。vpack...通道内压缩指令是Intel上的单个uop,它将2个寄存器以2:1的比例压缩为一个相同宽度的寄存器。如果在末尾执行vpermt2d混洗,它可能会领先于多个向量。因此,给定4个ZMM寄存器,4x vpmovdb在英特尔上将占用8个吞吐周期。4x vpand + 2x vpackssdw + 1x vpackuswb + vpermq将在Skylake/Cascade Lake或更高版本上每4个时钟周期生成一个准备存储的ZMM。(vpermt2w在Skylake-avx 512上可用,因此可以代替vpackuswb + vpermq使用,但不幸的是,在那里和更高版本的英特尔上,vpermt2w是3 uops。)AVX 2指令的另一个想法:* how to convert uint32 to uint8 using simd but not avx512? * -vpshufb(_mm256_shuffle_epi8)用于压缩和清零,以更宽的粒度设置2输入混洗,但每个输入向量需要一个port 5微操作。Intel的C/C++ intrinsic指南(https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html)有一个对asm助记符起作用的搜索;它们更短,更便于打字和谈论。
vpermt2b
vpack...
vpermt2d
vpermt2w
vpshufb
_mm256_shuffle_epi8
1条答案
按热度按时间5anewei61#
有一条指令
vpmovdb
(https://www.felixcloutier.com/x86/vpmovdb:vpmovsdb:vpmovusdb),但不幸的是,Intel CPU将其作为2个微指令运行,都是针对端口5的。https://uops.info/和https://agner.org/optimize/)。C内部函数有趣的事实:它允许内存 * 目的地 *,并且只需要AVX 512 F,而不是AVX 512 BW,所以这就是KNL至强融核可以进行字节粒度掩码存储的方式。有符号和无符号饱和形式,但您需要截断形式。
截断和跨道填充都是AVX-512中的新技术;在AVX 2中,通常必须使用2x
vpackssdw
来馈送vpackuswb
和vpermq
修复,还需要在打包之前屏蔽32位数据,使其位于0..255无符号范围内,这将为每个 input 向量额外消耗vpand
。带有多个向量
如果您有多个数据矢量,使用AVX-512 VBMI(Ice Lake / Zen 4)
vpermt2b
从两个矢量中获取字节可能是值得的。在支持它的Intel CPU上,这将运行3个uop(2 p5 + p015),因此每个256位矢量2个周期,而不是每个128位矢量2个周期。它在Zen 4上是1 uop,和
vpmovdb
一样,所以vpermt2b
是理想的,每个时钟允许一个256位存储,速度是Intel的两倍,前端uop更少。vpack...
通道内压缩指令是Intel上的单个uop,它将2个寄存器以2:1的比例压缩为一个相同宽度的寄存器。如果在末尾执行vpermt2d
混洗,它可能会领先于多个向量。因此,给定4个ZMM寄存器,4x
vpmovdb
在英特尔上将占用8个吞吐周期。4xvpand
+ 2xvpackssdw
+ 1xvpackuswb
+vpermq
将在Skylake/Cascade Lake或更高版本上每4个时钟周期生成一个准备存储的ZMM。(
vpermt2w
在Skylake-avx 512上可用,因此可以代替vpackuswb
+vpermq
使用,但不幸的是,在那里和更高版本的英特尔上,vpermt2w
是3 uops。)AVX 2指令的另一个想法:* how to convert uint32 to uint8 using simd but not avx512? * -
vpshufb
(_mm256_shuffle_epi8
)用于压缩和清零,以更宽的粒度设置2输入混洗,但每个输入向量需要一个port 5微操作。Intel的C/C++ intrinsic指南(https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html)有一个对asm助记符起作用的搜索;它们更短,更便于打字和谈论。