我有一个循环,进行一些计算,然后将符号位存储到 vector 中:

uint16x8_t rotate(const uint16_t* x);

void compute(const uint16_t* src, uint16_t* dst)
{
    uint16x8_t sign0 = vmovq_n_u16(0);
    uint16x8_t sign1 = vmovq_n_u16(0);
    for (int i=0; i<16; ++i)
    {
        uint16x8_t r0 = rotate(src++);
        uint16x8_t r1 = rotate(src++);
        // pseudo code:
        sign0 |= (r0 >> 15) << i;
        sign1 |= (r1 >> 15) << i;
    }
    vst1q_u16(dst+1, sign0);
    vst1q_u16(dst+8, sign1);
}

在该伪代码之后的 NEON 中累积符号位的最佳方法是什么?

Here's what I came up with:
    r0 = vshrq_n_u16(r0, 15);
    r1 = vshrq_n_u16(r1, 15);
    sign0 = vsraq_n_u16(vshlq_n_u16(r0, 15), sign0, 1);
    sign1 = vsraq_n_u16(vshlq_n_u16(r1, 15), sign1, 1);

另外,请注意,“伪代码”实际上是有效的,并且在性能方面几乎生成相同的代码。这里有什么可以改进的?注意,在实际代码中,循环中没有函数调用,因此我整理了实际代码以使其易于理解。
另一点:在 NEON 中,不能将变量用于 vector 移位(例如i不能用于指定移位数)。

最佳答案

ARM可以在一个 vsri 指令中完成此操作(感谢@ Jake'Alquimista'LEE)。

给定要从中获取符号位的新 vector ,将每个元素的低15位用累加器右移1。

您应该按2展开,这样编译器就不需要mov指令将结果复制回相同的寄存器,因为vsri是2个操作数的指令,在这里我们使用它的方式可以使我们得到不同的结果比旧的sign0累加器注册。

sign0 =  vsriq_n_u16(r0, sign0, 1);
// insert already-accumulated bits below the new bit we want

插入15次之后(或者如果从sign0 = 0开始而不是剥离第一个迭代并使用sign0 = r0开始,则为16),sign0的所有16位(每个元素)将是r0值中的符号位。

先前的建议:与 vector 常数AND隔离符号位。它比两类制效率更高。

您使用VSRA进行累加以转移累加器并添加新位的想法很好,因此我们可以保留该累加器,最多减少2条指令。
tmp = r0 & 0x8000;            // VAND
sign0 = (sign0 >> 1) + tmp;   // VSRA

或使用 NEON 内在函数:
uint16x8_t mask80 = vmovq_n_u16(0x8000);
r0 = vandq_u16(r0, mask80);        // VAND
sign0 = vsraq_n_u16(r0, sign0, 1); // VSRA

您可以根据需要使用内在函数或asm来实现,并以相同的方式编写标量版本,以使编译器有更好的机会进行自动向量化。

这确实需要寄存器中的 vector 常量。如果您对寄存器的要求非常严格,那么2个移位可能会更好,但是除非ARM芯片通常在SIMD桶形移位器上花费大量房地产,否则总计3个移位似乎会成为移位器吞吐量的瓶颈。

在这种情况下,可以使用这种通用SIMD想法,而无需ARM shift + accumulate或shift + insert
tmp = r0 >> 15;     // logical right shift
sign0 += sign0;     // add instead of left shifting
sign0 |= tmp;       // or add or xor or whatever.

这会给您以相反顺序的位。如果您可以按相反的顺序生产它们,那就太好了。

否则,ARM是否具有SIMD位反转或仅用于标量? (以相反的顺序生成并在最后翻转它们,为每个 vector 位图做一些额外的工作,希望只有一条指令。)

更新:是的,AArch64具有rbit,因此您可以反转一个字节中的位,然后按字节顺序将它们按正确的顺序排列。 x86可以使用pshufb LUT对两个4位块中的字节进行位反转。但是,随着您在x86上的积累,这可能无法做更多的工作。

关于c++ - 在 ARM NEON 中有效地积累符号位,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/49918746/

10-12 14:51