SIMD:更通用的随机播放功能

时间:2019-10-28 20:53:59

标签: c++ intel simd intrinsics avx

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, -, -, -, -}(其中-代表无关的值,并且它们可以是任何值)。 因此,我希望将int1设置位置的位置放在shuffled中。

(在我们的示例中:17、20、25、26坐在1中带有imm8的位置)。


英特尔提供了这种功能吗? 这样的功能如何有效地实现?


编辑:-可以忽略。只需设置位1的int。

1 个答案:

答案 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/qvpexpandd/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_psvshufps ymm, ymm, ymm, imm8)等较宽的4x2车道内混洗,相同的4x2位选择器模式将在两个通道中重复使用。对于_mm256_shuffle_pdvshufpd ymm, ymm, ymm, imm8)之类的2x1较宽的通道内混洗,我们得到4个1位立即数字段,这些字段仍选择通道内。

存在带有4个2位选择器vpermqvpermpd的跨界洗牌。它们的工作原理与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输入混洗可以分别处理输出的下半部分和上半部分,例如输出的低半部分可以从第一个源寄存器的元素中选择,输出的高半部分可以从第二个源寄存器的元素中选择。