在int32_t
的情况下,我认为SIMD混洗功能为not real shuffle,左右部分将分别混洗。
我想要一个真正的随机播放功能,如下所示:
假设我们得到了__m256i
,我们想改组8 int32_t
。
__m256i to_shuffle = _mm256_set_epi32(17, 18, 20, 21, 25, 26, 29, 31);
const int imm8 = 0b10101100;
__m256i shuffled _mm256_shuffle(to_shuffle, imm8);
我希望shuffled = {17, 20, 25, 26, -, -, -, -}
(其中-
代表无关的值,并且它们可以是任何值)。
因此,我希望将int
与1
设置位置的位置放在shuffled
中。
(在我们的示例中:17、20、25、26坐在1
中带有imm8
的位置)。
英特尔提供了这种功能吗? 这样的功能如何有效地实现?
编辑:-
可以忽略。只需设置位1
的int。
答案 0 :(得分:1)
(我假设您立即倒退(17
的选择器应该是低位,而不是高位),并且您的向量实际上是以低元素优先顺序写入的。)
如何有效地实现这种功能?
在这种情况下,使用AVX2 vpermd
(_mm256_permutevar8x32_epi32
)。它需要一个不是立即数的控制向量,才能为8个输出元素保留8个选择器。因此,您必须加载一个常数并将其用作控制操作数。
由于只关心输出向量的下半部分,因此向量常数只能为__m128i
,从而节省了空间。 vmovdqa xmm, [mem]
零扩展到相应的YMM向量。用内在函数用C编写此代码可能很不方便,但是_mm256_castsi128_si256
应该有效。甚至是_mm256_broadcastsi128_si256
,因为广播负载同样便宜。不过,有些编译器可能会通过进行常量传播将其简化为内存中的实际32字节常量。如果您知道汇编语言,则编译器输出经常令人失望。
如果要在源中获取实际的整数位图,则可以使用C ++模板在编译时将其转换为正确的向量常量。 Agner Fog's Vector Class Library(现在是Apache许可的,以前是GPL)具有一些相关的功能,例如,使用C ++模板,根据常量和支持的目标ISA将整数常量转换为单个混合或混合指令序列。但是其随机播放模板采用的是索引列表,而不是位图。
但是我想您是想问一问为什么x86 shuffle的设计方式/设计方式。
英特尔提供了这种功能吗?
是的,在带有AVX512F的硬件中(加上AVX512VL可在256位向量上使用它)。
您正在寻找 vpcompressd
,它是BMI2 pext
的向量元素等效项。 (但是它将控制操作数作为掩码寄存器值,而不是立即数。)内在函数是
__m256i _mm256_maskz_compress_epi32( __mmask8 c, __m256i a);
也可以使用合并到现有向量底部而不是将顶部元素清零的版本。
作为立即随机播放,
所有x86随机播放都使用控制操作数,该操作数具有指向源的索引,而不是保留哪些元素的位图。 (vpcompressd/q
和vpexpandd/q
除外)。或者,他们使用隐式控件,例如_mm256_unpacklo_epi32
,它从2个输入(低半和高半部分的行内)交错插入32位元素。
如果您要提供带有控制操作数的随机播放,那么如果任何元素可以在任何位置结束,通常通常是最有用的。因此,输出不必与输入顺序相同。您的压缩随机播放不具有该属性。
此外,对每个输出元素都有一个源索引是随机播放硬件自然想要的。我的理解是,每个输出元素都由其自己的MUX(多路复用器)馈送,其中MUX需要N个输入元素和一个二进制选择器来选择要输出的那个。 (它的宽度当然与元素的宽度一样。)有关构建多路复用器的更多讨论,请参见Where is VPERMB in AVX2?。
将控制操作数设置为选择器列表以外的某种格式,则需要对其进行预处理,然后才能将其馈送到洗牌硬件中。
对于立即数,格式为2x1位或4x2位字段,或者为_mm_bslli_si128
和_mm_alignr_epi8
的字节移位计数。或insertps
的索引+调零位掩码。没有立即宽度大于8位的SIMD指令。 大概可以使硬件解码器保持简单。
(或对于vextractf128 xmm, ymm, 0 or 1
使用1x1位,这在事后看来将是更好的选择,根本不需立即使用。与0
配合使用总是比vmovdqa xmm, xmm
差。尽管AVX512确实使用了与vextractf32x4
相同的操作码,其EVEX前缀为1x2位立即数,因此这可能对解码器的复杂性有所帮助。无论如何,没有任何选择范围大于2位的立即改编因为8x 3位将是24位。)
对于_mm256_shuffle_ps
(vshufps ymm, ymm, ymm, imm8
)等较宽的4x2车道内混洗,相同的4x2位选择器模式将在两个通道中重复使用。对于_mm256_shuffle_pd
(vshufpd ymm, ymm, ymm, imm8
)之类的2x1较宽的通道内混洗,我们得到4个1位立即数字段,这些字段仍选择通道内。
存在带有4个2位选择器vpermq
和vpermpd
的跨界洗牌。它们的工作原理与pshufd xmm
(_mm_shuffle_epi32
)相似,但是在256位寄存器中使用4个qword元素,而不是在128位寄存器中使用4个dword元素。
就缩小范围/仅关心部分输出而言:
普通立即数将需要4个3位选择器来为8个32位源元素之一的每个索引建立索引。但是更有可能是8个3位选择器= 24位,因为为什么要设计一个只能写一半半宽度输出的混洗指令? (除vextractf128 xmm, ymm, 1
之外。)
通常,更细粒度的混洗的范例是采用控制向量,而不是一些时髦的立即编码。
AVX512确实添加了一些变窄的改编,例如VPMOVDB xmm/[mem], x/y/zmm
,可将32位元素截断(或带符号/无符号饱和)到8位。 (以及所有其他尺寸组合)。
它们很有趣,因为它们带有内存目的地。也许这是由于某些没有AVX512VL的CPU(例如至强融核KNL / KNM)引起的,因此它们只能 使用带有ZMM向量的AVX512指令。它们仍然具有AVX1和2,因此您可以压缩为xmm reg,并使用常规的VEX编码存储。但这确实允许使用AVX512F进行窄字节掩码存储,只有将打包数据存储在XMM寄存器中的情况下,AVX512BW才可能实现。
有些类似shufps
的2输入混洗可以分别处理输出的下半部分和上半部分,例如输出的低半部分可以从第一个源寄存器的元素中选择,输出的高半部分可以从第二个源寄存器的元素中选择。