TL:DR: 你可能需要多次洗牌来处理车道交叉,或者如果你的模式完全像那样,你可以使用
_mm256_cvtepu16_epi32
(
vpmovzxwd
),然后使用
_mm256_blend_epi16
。
对于x86洗牌(像大多数SIMD指令集一样),目标位置是隐含的。 洗牌控制常量只有按目标顺序排列的源索引,无论它是一个编译+组装成汇编指令的imm8还是一个向量,其中每个元素都有一个索引。
每个目标位置只读取一个源位置,但同一源位置可以被多次读取。每个目标元素从洗牌源获取一个值。
请参见
Convert _mm_shuffle_epi32 to C expression for the permutation?以获取
dst = _mm_shuffle_epi32(src,_MM_SHUFFLE(d,c,b,a))
的纯C版本,展示了如何使用控制字节。
(对于pshufb /
_mm_shuffle_epi8
,高位设置为零的元素会将该目标位置清零,而其他x86洗牌则忽略洗牌控制向量中的所有高位。)
没有AVX512合并掩码,就没有混洗也能混合到目标的操作。有一些双源混洗操作,如
_mm256_shuffle_ps
(
vshufps
),可以将两个来源的元素混洗在一起以生成单个结果向量。如果您想留下一些未写入的目标元素,您可能需要先进行混洗,然后再进行混合,例如使用
_mm256_blendv_epi8
,或者如果您可以使用16位粒度混合,则可以使用更有效的立即混合
_mm256_blend_epi16
,甚至更好的是
_mm256_blend_epi32
(在英特尔CPU上,AVX2
vpblendd
与
_mm256_and_si256
的成本相同,并且是最佳选择,如果确实需要混合,则可以完成工作;请参见
http://agner.org/optimize/)。
对于你的问题(在Cannonlake中没有AVX512VBMI
vpermb
),
你无法使用单个操作将低16“lane”的单个字节洗牌到__m256i
向量的高16“lane”中。
AVX洗牌不像完整的256位SIMD,它们更像并行的两个128位操作。唯一的例外是某些带有32位粒度或更大的AVX2跨越lane的洗牌,例如
vpermd
(
_mm256_permutevar8x32_epi32
)。还有AVX2版本的
pmovzx
/
pmovsx
,例如
pmovzxbq
将XMM寄存器的低4字节零扩展为YMM寄存器的4个qword,而不是YMM寄存器的每半部分的低2字节。这使得它在使用内存源操作数时更加有用。
但无论如何,pshufb
的AVX2版本(_mm256_shuffle_epi8
)会在256位向量的两个通道中进行两个独立的16x16字节洗牌。
你可能会需要像这样的东西:
__m256i shuffle_and_blend(__m256i dst, __m256i src)
{
const __m256i shuffle_control = _mm256_setr_epi8(
0, 1, -1, -1, 1, 2, ...);
__m256i shuffled = _mm256_shuffle_epi8(src, shuffle_control);
shuffled = _mm256_cvtepu16_epi32(src);
__m256i blended = _mm256_blendv_epi8(shuffled, dst, shuffle_control);
return blended;
}
注意,第二个16字节的
pshufb
编号重新从0开始。
__m256i
的两半可以不同,但它们不能读取另一半的元素。如果您需要在高通道中获取字节以从低通道中获取字节,则需要进行更多的混洗和混合(例如包括
vinserti128
或
vperm2i128
,或者可能是一个
vpermd
跨越字的混洗)以将所有所需的字节放入一个16字节组中并按某种顺序排列。
(实际上,
_mm256_shuffle_epi8
(PSHUFB)忽略混洗索引中的4..6位,因此写入
17
与
1
相同,但非常误导人。它有效地执行
%16
,只要高位未设置。如果混洗控制向量中的最高位设置了,则将其清零。我们不需要这个功能;
_mm256_blendv_epi8
不关心要替换的元素的旧值)
无论如何,这个简单的两个指令的例子只适用于模式不继续的情况。如果您想要设计真正的混洗,请提出更具体的问题。
而且,我注意到您的混合模式使用了2个新字节,然后跳过了2个。如果继续这样做,您可以使用
vpblendw
_mm256_blend_epi16
代替
blendv
,因为该指令在Intel CPU上只需1个uop即可运行,而不是2个。它还将使您能够使用AVX512BW
vpermw
,这是当前Skylake-AVX512 CPU中提供的16位洗牌,而不是可能更慢的AVX512VBMI
vpermb
。
或者实际上,它也许会让您使用
vpmovzxwd
(
_mm256_cvtepu16_epi32
)将16位元素零扩展为32位元素,作为跨通道的洗牌。然后与
dst
混合。