C++ SIMD:更通用的洗牌函数

C++ SIMD:更通用的洗牌函数,c++,intel,simd,intrinsics,avx,C++,Intel,Simd,Intrinsics,Avx,我认为SIMD shuffle功能适用于int32\u t情况,左右部分将分别进行洗牌 我想要一个真正的洗牌函数,如下所示: 假设我们得到了\uuuum256i,我们想洗牌8int32\u 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);

我认为SIMD shuffle功能适用于
int32\u t
情况,左右部分将分别进行洗牌

我想要一个真正的洗牌函数,如下所示:

假设我们得到了
\uuuum256i
,我们想洗牌8
int32\u 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,-,-,-,-}
,其中
-
表示不相关的值,它们可以是任何值。 因此,我希望设置位为
1
的位置处的
int
将被置于
shuffled

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


这样的功能是由英特尔公司提供的吗 如何有效地实施这一职能



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

(我假设您立即向后(17的选择器应该是低位,而不是高位),并且向量实际上是以低位元素的第一顺序写入的)

如何有效地实施这一职能

在这种情况下,使用AVX2
vpermd
\u mm256\u permutevar8x32\u epi32
)。它需要一个控制向量,而不是一个立即数,来为8个输出元素保存8个选择器。因此,您必须加载一个常量并将其用作控制操作数

由于只关心输出向量的下半部分,因此向量常数只能是
\uuum128i
,从而节省空间
vmovdqa xmm,[mem]
zero扩展到相应的YMM向量中。用C语言编写内部函数可能不太方便,但是
\u mm256\u castsi128\u si256
应该可以工作。甚至
\u mm256\u broadcastsi128\u si256
,因为广播负载同样便宜。不过,一些编译器可能会通过常量传播将其悲观化为内存中实际的32字节常量。若你们知道汇编,编译器的输出常常令人失望

如果你想在你的源代码中取一个实际的整数位图,你可以使用C++模板将它编译成正确的向量常量。(现在Apache授权的,以前的GPL)有一些相关的东西,像使用C++模板,根据常数和支持什么目标ISA,将整数常量转换成一个混合或一系列混合指令。但是它的洗牌模板是索引列表,而不是位图

但我想你是想问为什么/如何设计x86洗牌。


这样的功能是由英特尔公司提供的吗

是,在带有AVX512F的硬件中(加上AVX512VL在256位向量上使用)

您正在寻找,它是与BMI2
pext
等价的向量元素。(但它将控制操作数作为掩码寄存器值,而不是立即数常量。)内在值为
\uuuum256i\umm256\uMaskz\uCompress32(\uuuummask8 c,\uuuum256i a)
它也可用于合并到现有向量底部的版本,而不是将顶部元素归零


作为即时洗牌,不需要。

所有x86洗牌都使用一个在源中具有索引的控制操作数,而不是保留哪些元素的位图。(除了
vpcompressd/q
vpexpandd/q
)。或者,它们使用隐式控制,如
\u mm256\u unplo\u epi32
,例如,它将来自2个输入的32位元素交错(在低半部和高半部的通道中)

如果您打算提供一个带有控制操作数的洗牌,那么如果任何元素可以在任何位置结束,这通常是最有用的。因此,输出不必与输入的顺序相同。你的压缩洗牌没有那个属性

此外,为每个输出元素提供源索引是shuffle硬件自然想要的。我的理解是,每个输出元素都由它自己的MUX(多路复用器)馈送,其中MUX接受N个输入元素和一个二进制选择器来选择要输出的元素。(当然,宽度与元素宽度相同。)有关构建复用器的更多讨论,请参阅

如果控制操作数的格式不是选择器列表,则需要先进行预处理,然后才能将其送入洗牌硬件。

对于立即数,格式为2x1位或4x2位字段,或为
\u mm\u bslli\u si128
\u mm\u aligner\u epi8
的字节移位计数。或
insertps
的索引+调零位掩码。没有立即宽度大于8位的SIMD指令这可能使硬件解码器保持简单。

(或者对于
vextractf128 xmm,ymm,0或1
使用1x1位,事后来看,如果根本没有立即数,这会更好。将它与
0
一起使用总是比
vmovdqa xmm,xmm
更糟糕。虽然AVX512使用相同的操作码,但对于1x2位立即数使用EVEX前缀,因此这可能对解码器的复杂性有一些好处。)y、 无论如何,选择器字段宽度大于2位时不存在立即混洗,因为8x 3位将是24位。)

对于较宽的4x2车道内混洗,如
\u mm256\u shuffle\u ps
vshufps ymm、ymm、ymm、imm8
),相同的4x2位选择器模式可重复用于两条车道。对于较宽的2x1车道内混洗,如
\u mm256\u shuffle\u pd
(),我们获得仍在车道中选择的4x1位立即字段

有4个2位选择器、
vpermq
vpermpd
的车道交叉混洗。它们的工作原理与
pshufd xmm
mm\u shuffle\u epi32
)完全相同,但在256位寄存器上有4个qword元素,而不是在128位寄存器上有4个dword元素


就缩小范围/仅关注部分输出而言:

一个普通的立即数需要4个3位选择器来选择每个索引