使用 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)
将相同的值放入向量的两个元素中,而不是使用具有相同值的单独的 l
和 r
变量。
在_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,只需担心实际已知的硬件行为。)
我正在尝试在 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)
将相同的值放入向量的两个元素中,而不是使用具有相同值的单独的 l
和 r
变量。
在_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,只需担心实际已知的硬件行为。)