assembly 使用SIMD移位/循环字节向量的最快方法

bvn4nwqk  于 2022-11-13  发布在  其他
关注(0)|答案(2)|浏览(135)

我有一个avx 2(256位)SIMD字节向量,在前面和后面用零填充,如下所示:[0, 2, 3, ..., 4, 5, 0, 0, 0]。编译时不知道前面的零的数量。
我如何有效地移位/旋转零,使其看起来像这样:[2, 3, 4, 5, ..., 0, 0, 0, 0]

tyky79it

tyky79it1#

AVX 2无法执行粒度小于4字节的跨通道混洗。(在《冰湖》中)。如果你有那个,也许在面具上有vpcmpeqb/vpmovmskb/tzcnt,并将其作为偏移量从alignas(64) int8_t shuffles = {0,1,2,...,31, 0, 1, 2, ... 31};常量数组中加载一个32字节的窗口。这就是vpermb的shuffle控制向量。
如果没有AVX-512 VBMI,尽管存储转发会暂停,但存储两次并跨两次执行未对齐的重载 * 可能 * 是有意义的。如果您需要在许多其他工作之间为一个向量执行此操作,这对吞吐量是有好处的,但如果在没有太多其他工作的循环中执行此操作,这就不太好了。
Store-forwarding stalls don't pipeline with each other, but can pipeline with successful store-forwarding。因此,如果您只是偶尔需要一个向量,并且无序exec可以隐藏延迟,则无需向vpcmpeqb/tzcnt或lzcnt执行许多微操作即可获得加载偏移。

pw9qyyiw

pw9qyyiw2#

如果类型大于32位。

我不太理解关于_mm256_permutevar8x32_epi32的文档,但实际上,向恒等置换添加偏移量会进行旋转-这是您想要的(当您已经获得前导0的数量时)。

__m256i rotate_i32(__m256i w, int offset) {
    __m256i identity = _mm256_set_epi32(7, 6, 5, 4, 3, 2, 1, 0);
    __m256i shuffle = _mm256_add_epi32(identity, _mm256_set1_epi32(offset));
    return _mm256_permutevar8x32_epi32(w, shuffle);
}

神箭在这里:https://godbolt.org/z/Kv8oxs6oY

(-1, -2, -3, -4, -5, -6, -7, -8)
(-2, -3, -4, -5, -6, -7, -8, -1)
(-3, -4, -5, -6, -7, -8, -1, -2)
(-4, -5, -6, -7, -8, -1, -2, -3)
(-5, -6, -7, -8, -1, -2, -3, -4)
(-6, -7, -8, -1, -2, -3, -4, -5)
(-7, -8, -1, -2, -3, -4, -5, -6)
(-8, -1, -2, -3, -4, -5, -6, -7)

同样的技巧也适用于64位,但需要多次偏移2。

__m256i rotate_i64(__m256i w, int offset) {
    __m256i identity = _mm256_set_epi32(7, 6, 5, 4, 3, 2, 1, 0);
    __m256i shuffle = _mm256_add_epi32(identity, _mm256_set1_epi32(offset * 2));
    return _mm256_permutevar8x32_epi32(w, shuffle);
}

神箭:https://godbolt.org/z/85h6aWPsW
输出量:

(-1, -2, -3, -4)
(-2, -3, -4, -1)
(-3, -4, -1, -2)
(-4, -1, -2, -3)

相关问题