基于布尔掩码将元素移动到 SIMD 寄存器的左侧
Posted
技术标签:
【中文标题】基于布尔掩码将元素移动到 SIMD 寄存器的左侧【英文标题】:Shift elements to the left of a SIMD register based on boolean mask 【发布时间】:2015-02-26 05:57:09 【问题描述】:这个问题与此有关:Optimal uint8_t bitmap into a 8 x 32bit SIMD "bool" vector
我想用这个签名创建一个最优函数:
__m256i PackLeft(__m256i inputVector, __m256i boolVector);
所需的行为是在这样的 64 位 int 输入上:
inputVector = 42, 17, 13, 3
boolVector = true, false, true, false
它会屏蔽所有在boolVector
中具有false
的值,然后重新打包保留在左侧的值。在上面的输出中,返回值应该是:
42, 13, X, X
... X 是“我不在乎”。
一个明显的方法是使用 _mm_movemask_epi8
从 bool 向量中获取一个 8 字节的 int,在表中查找 shuffle 掩码,然后使用掩码进行 shuffle。
但是,如果可能,我想避免使用查找表。有更快的解决方案吗?
【问题讨论】:
相关:***.com/questions/18708232/… 和 ***.com/questions/25074197/… @Zboson:Hacker's Delight 中有一个关于此的部分(7-4 压缩,或广义提取,第一版中的 pp116-122) - 它实际上涵盖了做这在位级别,但相同的技术应该适用于字节级别,我想(我没有仔细研究过)。 @PaulR,我得到了 Kindle 版本。这本书太棒了!如果我以前有这个,我可以节省很多时间。 @Zboson:很高兴你喜欢它!既然你喜欢这个,你可能还会喜欢免费的 PDF 书 Matters Computational by Jörg Arndt - 它非常密集和深奥,但里面有一些好东西。 @Both:Hacker's Delight 真的很棒。强烈推荐。在书中,我相信他们称这个操作为 SAG = Sheep And Goats。 【参考方案1】:Andreas Fredriksson 在他的 2015 GDC 演讲中很好地介绍了这一点:https://deplinenoise.files.wordpress.com/2015/03/gdc2015_afredriksson_simd.pdf
从幻灯片 104 开始,他介绍了如何仅使用 SSSE3,然后仅使用 SSE2。
【讨论】:
With BMI2, you can generate masks on the fly for AVX2.【参考方案2】:刚刚看到这个问题 - 也许你已经解决了它,但仍在为可能需要处理这种情况的其他程序员编写逻辑。
解决方案(英特尔 ASM 格式)如下所示。它包括三个步骤:
步骤 0:将 8 位掩码转换为 64 位掩码,原始掩码中的每个设置位表示为扩展掩码中的 8 个设置位。
第 1 步:使用此扩展掩码从源数据中提取相关位
第 2 步:由于您需要保留数据,因此我们将输出移动适当的位数。
代码如下:
; Step 0 : convert the 8 bit mask into a 64 bit mask
xor r8,r8
movzx rax,byte ptr mask_pattern
mov r9,rax ; save a copy of the mask - avoids a memory read in Step 2
mov rcx,8 ; size of mask in bit count
outer_loop :
shr al,1 ; get the least significant bit of the mask into CY
setnc dl ; set DL to 0 if CY=1, else 1
dec dl ; if mask lsb was 1, then DL is 1111, else it sets to 0000
shrd r8,rdx,8
loop outer_loop
; We get the mask duplicated in R8, except it now represents bytewise mask
; Step 1 : we extract the bits compressed to the lowest order bit
mov rax,qword ptr data_pattern
pext rax,rax,r8
; Now we do a right shift, as right aligned output is required
popcnt r9,r9 ; get the count of bits set in the mask
mov rcx,8
sub cl,r9b ; compute 8-(count of bits set to 1 in the mask)
shl cl,3 ; convert the count of bits to count of bytes
shl rax,cl
;The required data is in RAX
相信这会有所帮助
【讨论】:
Never use the LOOP instruction 如果您希望您的代码快速运行。由于您无论如何都在使用 BMI2 PEXT,因此您不需要循环!您可以使用0x0101...
进行 PDEP 并乘以 0xFF
以将掩码中的每个位扩展为全 0 或全 1 的完整字节。
我认为您将八个 8 位整数左包装在一个 64 位整数中,这不是 OP 所要求的。不过,这种技术对于为 VPERMD 生成 shuffle-mask 很有用。请参阅my AVX2+BMI2 answer on a left-packing question,我使用 PDEP/PEXT + POPCNT 来执行此操作,与您的代码有一些相似之处。 (但我没有直接使用 PEXT 处理输入数据,而是在常量上使用它,然后使用 VPMOVZXBD 来获得随机掩码)。以上是关于基于布尔掩码将元素移动到 SIMD 寄存器的左侧的主要内容,如果未能解决你的问题,请参考以下文章
有没有一种有效的方法来使用 SIMD 内在函数来获取 SIMD 寄存器中的第一个非零元素?
使用 SIMD,我如何有条件地仅移动 alpha 通道值为 255 的像素?