使用 AVX2 指令左移 128 位数

left shift of 128 bit number using AVX2 instruction

我正在尝试在 AVX2 中对 128 位数字进行左旋转。由于没有直接的方法,我尝试使用左移和右移来完成我的任务。

这是我执行相同操作的代码片段。

        l = 4;
        r = 4;
        targetrotate = _mm_set_epi64x (l, r);
        targetleftrotate = _mm_sllv_epi64 (target, targetrotate);

上面的代码片段将目标向左旋转 4。
当我用样本输入测试上面的代码时,我可以看到结果没有正确旋转。

这是示例输入和输出

          input: 01 23 45 67 89 ab cd ef   fe dc ba 98 76 54 32 10
obtained output: 10 30 52 74 96 b8 da fc   e0 cf ad 8b 69 47 25 03

但是,我期望的输出是

                 12 34 56 78 9a bc de f0   ed cb a9 87 65 43 21 00

我知道我做错了什么。我想知道我的预期输出是否正确,如果正确,我想知道我在这里做错了什么。

任何形式的帮助都将不胜感激,并提前致谢。

我认为您在打印输入和输出的方式方面存在字节序问题。

每个 64 位半中最左边的字节是实际输出中最不重要的字节,因此 0xfe << 4 变为 0xe0,随着 f 移入更高字节。

请参阅 Convention for displaying vector registers 了解更多相关讨论。

您的 "expected" 输出与您先打印值高元素(存储时的最高地址)时得到的结果相匹配。但这不是你在做的;您正在按升序内存顺序分别打印每个字节。 x86 是小端。这与我们在英语中使用的数字系统冲突,在英语中我们从左到右阅读阿拉伯数字,左边的最高位值,实际上是人类的 big-endian。有趣的事实:阿拉伯语是从右到左阅读的,所以对他们来说,书写数字是 "human little-endian".

(并且跨元素,更高的元素位于更高的地址;首先打印高 元素 使得像 _mm_bslli_si128 aka pslldq 这样的全向量移位变得有意义以它在元素之间向左移动字节的方式。)

如果您使用的是调试器,您可能正在其中进行打印。如果您正在使用调试打印,请参阅 print a __m128i variable


顺便说一句,您可以使用 _mm_set1_epi64x(4) 将相同的值放入向量的两个元素中,而不是使用具有相同值的单独的 lr 变量。

_mm_set intrinsics中,高位元素排在前面,匹配Intel的asm手册中的图,匹配"left" shift moving的语义bits/bytes 向左。 (例如,参见英特尔的图表 pshufd, _mm_shuffle_epi32 的元素编号)


顺便说一句,AVX512 有 vprolvq 个旋转。但是,是的,要模拟旋转,您需要 (x << n) | x >> (64-n) 的 SIMD 版本。请注意,x86 SIMD 移位 饱和 移位计数,这与 屏蔽 计数的标量移位不同。所以 x >> 64 将移出所有位。如果你想支持 63 以上的旋转计数,你可能需要屏蔽。

(Best practices for circular shift (rotate) operations in C++ 但您使用的是内部函数,因此您不必担心 C 移位计数 UB,只需担心实际已知的硬件行为。)