一些快速的 __m256i 位运算

问题描述 投票:0回答:1

我正在寻找快速代码来在

__m256i
上执行以下操作,并且希望得到帮助:

  • 清除最低有效位(设置的最低有效位)
  • 清除最高有效位(设置的最高有效位)
  • 清除位置
    int i
    处的位(其中
    0 <= i < 256
  • 找到最低有效位的位置(返回
    int
    ,可以假设非零输入)
  • 找到最高有效位的位置(返回
    int
    ,可以假设非零输入)
x86 simd intrinsics avx
1个回答
0
投票

部分问题已在之前的问答中解决:

  • 在大型数组中高效查找最低有效设置位? - 在循环查找非零

    __m256i
    后查找非零
    __m256i
    中的最低设置位。使用相反方向的位扫描指令,相同的算法可以找到最高设置位。

  • 在 AVX 寄存器(__m256i)中设置单独的位,需要“随机访问”运算符 - @wim 使用

    vpsllvd
    的答案非常聪明,生成一个恰好设置了 1 位的向量。当移位计数超出范围时,SIMD 会将元素移位为零,因此
    set1(count) - setr(0, 32, 64, 96, ...)
    会生成一个只有一个在范围内的移位计数的向量,您可以使用它来移位
    set1(1)
    的向量。

    清除而不是设置所选位只是用

    _mm256_andnot_si256
    而不是
    or
    1<<n
    向量的问题。或者
    xor
    翻转该位。

    (ermlg 的答案有一个存储/重新加载存储转发停顿,但如果在执行之间存在大量周围代码,则可能对吞吐量没有坏处,因为存储转发停顿无法在 SnB 系列上相互管道化,但可以通过成功的存储转发进行管道传输。)

  • clear_nth(v, bitscan_forward/reverse(v))
    使用上述策略可能是清除整个
    __m256i
    中最高或最低设置位的最不坏的方法。我不认为有一个好的方法可以做到这一点,所以理想情况下,设计你的算法只需要在较小的块中进行此操作。

    使用 AVX-512,您可以测试掩码 (

    vptestmd k1, ymm0,ymm0
    ) 和
    kmov
    /
    blsi
    /
    kmov
    ,以隔离合并掩码的最低设置位
    v &= v-1
    (
    vpaddd
    /
    vpandd) 
    )。因此,只有包含最低位的元素才会改变,因为只有该位具有非零掩码。 (在两个
    ksub
    指令中没有
    kneg
    mask &= -mask
    来实现
    k
    ;您必须使用 2 的补码恒等式,例如
    m &= ~m+1
    knotw
    /
    kaddw
    /
    kandw
    已经设置了
    __mmask16 k1 = 1;
    掩码常量。由于每个
    k
    指令仅在 Intel 上的一个端口上运行,但 BMI1
    blsi
    甚至可以在端口 1 或 5 上运行,因此最好将
    kmov
    绕过它。并且无论您的意愿如何,编译器都可能会这样做。)


__m256i
不像单个 256 位整数那样工作。对于像
blsr
这样带有
x &= x-1
位黑客的东西,尝试在 SIMD 元素之间传播进位是非常不方便的;你必须做点别的事情。

搜索功能非常可行,不过,使用

_mm256_cmpeq_epi8
反对零和
_mm256_movemask_epi8
/
tzcnt
31-lzcnt
(或 Intel 上的
bsr
,速度不慢),同时将向量存储到 tmp缓冲区,以便您可以索引相关字节并对其进行位扫描。或者在更宽的块中使用
_mm256_movemask_ps
上的
_mm256_cmp_epi32
比较蒙版。请参阅上面链接的问答。

也许还有相关的构建块:

© www.soinside.com 2019 - 2024. All rights reserved.