有效累积手臂霓虹灯中的符号位

时间:2018-04-19 10:26:00

标签: c++ arm intrinsics neon

我有一个执行某些计算的循环,然后将符号位存储到向量中:

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);
}

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

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);

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

1 个答案:

答案 0 :(得分:1)

ARM可以在一条vsri指令中执行此操作(感谢@ Jake'Alquimista'LEE)。

给定一个新的向量,你想要符号位来自,将每个元素的低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开头,则为16次,而不是剥离第一次迭代并使用sign0 = r0),sign0的所有16位(每个元素)将是来自r0值。

以前的建议:使用向量常量来隔离符号位。它比两班倒更有效率。

你想用VSRA积累移位累加器并添加新位是好的,所以我们可以保留它并总共下达2条指令。

tmp = r0 & 0x8000;            // VAND
sign0 = (sign0 >> 1) + tmp;   // VSRA

或使用霓虹内在函数:

uint16x8_t mask80 = vmovq_n_u16(0x8000);
r0 = vandq_u16(r0, mask80);        // VAND
sign0 = vsraq_n_u16(r0, sign0, 1); // VSRA

使用intrinsics或asm实现你喜欢的,并以相同的方式编写标量版本,为编译器提供更好的自动向量化机会。

这确实需要寄存器中的向量常量。如果你对寄存器非常紧张,那么2个班次可能会更好,但总共3个班次似乎可能会对移动器的吞吐量造成瓶颈,除非ARM芯片通常在SIMD桶形移位器上花费大量的空间。

在这种情况下,也许使用这种通用SIMD理念,无需ARM移位+累积或移位+插入

tmp = r0 >> 15;     // logical right shift
sign0 += sign0;     // add instead of left shifting
sign0 |= tmp;       // or add or xor or whatever.

这会为您提供相反顺序的位。如果你能以相反的顺序生产它们,那么很棒。

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

更新:是的,AArch64有rbit,因此您可以在一个字节内反转位,然后按字节随机方式将它们按正确的顺序排列。 x86可以使用pshufb LUT在两个4位块中的字节内进行位反转。但是,当您在x86上累积位时,这可能不会在执行更多工作之前出现。