我正在尝试在AVX2中向左旋转128位数字。由于没有这样做的直接方法,因此我尝试使用左移和右移来完成任务。
这是我的代码的一小段。
l = 4;
r = 4;
targetrotate = _mm_set_epi64x (l, r);
targetleftrotate = _mm_sllv_epi64 (target, targetrotate);
上面的code代码片段将目标向左旋转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
我知道我做错了什么。我想知道我的预期输出是否正确,如果是,我想知道我在这里做错了什么。
任何帮助将不胜感激,并在此先感谢。
答案 0 :(得分:2)
我认为您在打印输入和输出方式方面有一个字节序问题。
每个64位半部分中最左边的字节是实际输出中最低有效的字节,因此0xfe << 4
变为0xe0
,而{{1 }}移到更高的字节。
有关此内容的更多讨论,请参见Convention for displaying vector registers。
您的“预期”输出与您首先打印高元素(存储时的最高地址)的值匹配。但这不是你在做什么;您将按升序分别打印每个字节。 x86是Little-endian。这与我们在英语中使用的数字系统相冲突,在英语中,我们从左到右读取阿拉伯数字,在左侧是最高的位数值,实际上是人类的大端数字。有趣的事实:阿拉伯语从右到左阅读,因此对于他们来说,书面数字是“人类小尾数”。
(并且在元素之间,较高的元素位于较高的地址;首先打印较高的 elements 会使整个向量移位,如f
或_mm_bslli_si128
那样有意义元素之间还剩字节。)
如果使用调试器,则可能在其中进行打印。如果您使用的是调试打印,请参见print a __m128i variable。
顺便说一句,您可以使用pslldq
将相同的值放入向量的两个元素中,而不是使用具有相同值的单独的_mm_set1_epi64x(4)
和l
变量。
在r
内在函数中,高位元素排在第一位,与英特尔的asm手册中的图匹配,并且与“左”的语义匹配(向左移动)。 (例如,参见英特尔图解,pshufd, _mm_shuffle_epi32
的元素编号)
顺便说一句,AVX512旋转了vprolvq
。但是,是的,要模拟旋转,您需要SIMD版本_mm_set
。请注意,x86 SIMD将移位计数饱和,与标量移位屏蔽的计数不同。因此(x << n) | x >> (64-n)
将移出所有位。如果要支持63以上的轮换计数,则可能需要屏蔽。
({Best practices for circular shift (rotate) operations in C++,但您使用的是内部函数,因此您不必担心C移位计数UB,而不必担心实际的已知硬件行为。)