c++ - 使用 AVX2 指令左移 128 位数

标签 c++ simd intrinsics avx avx2

我正在尝试在 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对此进行更多讨论。

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

(并且跨元素,更高的元素位于更高的地址;首先打印高元素使得像_mm_bslli_si128又名pslldq这样的全 vector 移位在元素之间向左移动字节的方式上有意义.)

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


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

_mm_set内在函数,高位元素在前,与Intel的asm手册中的图表相匹配,并与“左”的语义相匹配移动位/字节向左移动。 (例如,参见英特尔图表 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,只需担心实际已知的硬件行为。)

关于c++ - 使用 AVX2 指令左移 128 位数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59123157/

相关文章:

C++ 非静态数据成员初始化器,只是有点困惑

c++ - QPdfWriter 和页面大小

c - 英特尔内部函数 : multiply interleaved 8bit values

c++ - 在 C++ 中自动将内部类型转换为枚举

simd - SIMD 内在函数的引用手册/教程?

c++ - 为什么在实践中向右移动在 Neon 和 SSE 中向左移动(反之亦然)?

c++ - 值是如何返回的? - 递归算法

c++ - Vulkan vkCreateWin32SurfaceKHR 加载失败

c - 替代 immintrin.h

arm - 如何使用 NEON 比较(大于或等于)指令?