基于布尔掩码将元素移动到 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 寄存器的左侧的主要内容,如果未能解决你的问题,请参考以下文章

如何在 python 中使用掩码将特定元素归零?

有没有一种有效的方法来使用 SIMD 内在函数来获取 SIMD 寄存器中的第一个非零元素?

使用 SIMD,我如何有条件地仅移动 alpha 通道值为 255 的像素?

html 使用没有javascript的svg掩码将一个图像掩盖在另一个上面

通过布尔掩码数组选择numpy数组的元素

SIMD (AVX2) - 将 uint8_t 值加载到多个浮点 __m256 寄存器