C语言 最小值/最大值的寄存器内SIMD版本

yzxexxkh  于 2023-01-20  发布在  其他
关注(0)|答案(1)|浏览(153)

假设我有两个uint16_t[4]数组ab,数组中的每个整数都在[0,16383]范围内,所以第14位和第15位没有设置,然后我用一些代码来查找x1m5 n1,在a[i]b[i]中的最小值和最大值:

uint16_t min[4], max[4];
for (int i = 0; i < 4; i++) {
    if (a[i] < b[i]) {
        min[i] = a[i];
        max[i] = b[i];
    } else {
        min[i] = b[i];
        max[i] = a[i];
    }
}

假设由于某种原因,我不能/不愿使用SIMD,但我仍然希望在64位平台上尽可能快地进行计算,因此,自然的解决方案是在64位寄存器上使用单寄存器内SIMD(SWAR)范例,在单次迭代中计算这4个值,而不是使用16位算法进行4次迭代。
使用SWAR范例时,可以使用哪些位处理技巧来实现(min或max)操作,或者理想情况下同时实现这两种操作,从而使生成的代码比上面的循环更快?我的目标架构是ARMv 8,因此可以随意使用任何有助于减少指令数的ARMv 8指令。
汇编或C +内联汇编解决方案都是受欢迎的。

c3frrgcw

c3frrgcw1#

您可以使用这样的代码,尽管它实际上比使用SIMD要长得多:

orr     x2, x0, #0x8000800080008000     // x2 = 0x8000 | x0
sub     x2, x2, x1                      // x2 = (0x8000 | x0) - x1
and     x2, x2, #0x8000800080008000      // x2 = x0 < x1 ? 0x0000 : 0x8000
mov     x3, #0x7fff7fff7fff7fff
add     x2, x3, x2, lsr #15             // x2 = x0 < x1 ? 0x7fff : 0x8000
eor     x4, x0, x1                      // x4 = x0 ^ x1
and     x3, x4, x2                      // x3 = x0 < x1 ? x0 ^ x1 : 0x0000
eor     x4, x1, x3                      // x4 = x0 < x1 ? x0 : x1
eor     x3, x0, x3                      // x3 = x0 < x1 ? x1 : x0

该算法的关键路径有6条指令,

mov     x3, #0x7fff7fff7fff7fff
eor     x4, x0, x1                      // x4 = x0 ^ x1

不在关键路径上。如果在循环中执行,则可能会提升恒定负载。最后两条指令可以独立评估,以相同的延迟产生最小值和最大值。

相关问题