将指针取消引用的 SIMD 向量化为 bool 数组、AND/NAND 结果

Hug*_*ira 5 c++ performance simd vectorization

我正在尝试优化以下简单算法,针对 x86-64 和 arm64 中的 SIMD 指令。给定一个T位向量,以及两个AidxBidx大小相同的向量T,包含 中值的索引T,函数将T'使用以下算法生成新的:

A = T[Aidx]
B = T[Bidx]
T' = A NAND B
Run Code Online (Sandbox Code Playgroud)

这是一个 C++ 实现:

#include <cstddef>

#define align 128
#define vsize 1024

struct alignas(align) vec_friendly_idx {
    const static std::size_t size = vsize;
    unsigned short v[size];
};

struct alignas(align) vec_friendly_bool {
    const static std::size_t size = vsize;
    unsigned char v[size];
};

void vecadd3(vec_friendly_bool& __restrict t, const vec_friendly_idx& aidx, const vec_friendly_idx& bidx, vec_friendly_bool& __restrict a, vec_friendly_bool& __restrict b) {
    for (std::size_t i = 0; i != vec_friendly_idx::size; i++) {
        a.v[i] = t.v[aidx.v[i]];
        b.v[i] = t.v[bidx.v[i]];
    }

    for (std::size_t i = 0; i != vec_friendly_idx::size; i++) {
        t.v[i] = !(a.v[i] & b.v[i]);
    }
}
Run Code Online (Sandbox Code Playgroud)

GCC 生成以下 arm64 代码:

        mov     x5, 0
.L2:
        ldrh    w7, [x1, x5, lsl 1]
        ldrh    w6, [x2, x5, lsl 1]
        ldrb    w7, [x0, w7, sxtw]
        ldrb    w6, [x0, w6, sxtw]
        strb    w7, [x3, x5]
        strb    w6, [x4, x5]
        add     x5, x5, 1
        cmp     x5, 1024
        bne     .L2
        movi    v2.16b, 0x1
        mov     x1, 0
.L3:
        ldr     q0, [x4, x1]
        ldr     q1, [x3, x1]
        and     v0.16b, v0.16b, v1.16b
        cmeq    v0.16b, v0.16b, #0
        and     v0.16b, v2.16b, v0.16b
        str     q0, [x0, x1]
        add     x1, x1, 16
        cmp     x1, 1024
        bne     .L3
        ret
Run Code Online (Sandbox Code Playgroud)

验收主要问题:

Q1. 关于如何进一步优化这个的任何想法?

理解奖励问题:

Q2。虽然 NAND 计算明显矢量化(因此仅执行 64 次迭代),但矢量索引却不是(执行 1024 次迭代)。我的印象是收集-收集指令在这里会很有帮助;但也许我错了?

Q3。我还考虑过将这些位打包起来T可以让 NAND 更快(现在,7 位是垃圾),但我有一种感觉,必要的打包/解包开销不会导致更快的执行?