assembly 在64位处理器上交换4个16位整数的最有效方法是什么?

7vux5j2d  于 2022-11-13  发布在  其他
关注(0)|答案(3)|浏览(128)

我有四个uint16,分别命名为a、B、c、d,现在我想这样交换它们:

void swap4(uint16_t &a, uint16_t &b, uint16_t &c, uint16_t &d) {
    uint16_t temp = a;
    a = b;
    b = c;
    c = d;
    d = temp;
}

我能做些什么来加快这个过程吗?

4dbbbstv

4dbbbstv1#

在C++中,这是

void swap4(uint16_t &a, uint16_t &b, uint16_t &c, uint16_t &d) {
    std::tie(a, b, c, d) = std::make_tuple(b, c, d, a);
}
kxkpmulp

kxkpmulp2#

那么,代码应该按照最佳的汇编指令序列编译,假设最低的优化级别。也就是说,如果允许别名,否则请查看273k,它在写入所有值之前加载所有值。
但是它太短了,而且有太多的间接性,所以无论如何这不是所有优化潜力所在。
内联和优化产生的更大的块有更大的影响。而且,它将允许编译器查看是否发生别名。
好消息是,如果你允许编译器,它会完成这一点,这要归功于lto、整体程序优化等(因此取决于正确调用编译器),或者定义与调用在同一个TU中(这可能会调用将其作为头中的内联函数,或将其标记为静态)。

dfddblmv

dfddblmv3#

如前所述,首先要确保这确实是一个瓶颈:大多数编译器都应该为此生成高效的代码(除非参数之间可能存在别名)。
如果这些16位值是连续存储在内存中的(例如,这是一个四元素向量),那么(a)确保它们在右边界对齐!(b)你可以使用CPU的shuffle指令,这是一个优化,你的编译器可能会也可能不会自己识别。在你进一步操作之前,检查你的编译器的汇编输出;带有-O2的现代GCC实际上会自动识别这种简化(https://godbolt.org/z/qo1jxnbds)。
如果你真的想手动滚动,GCC为此提供了一个可移植的__builtin_shuffle宏;对于您用例,您可以编写

typedef uint16_t quadword __attribute__ ((vector_size (8)));
quadword input = {a, b, c, d};
const quadword rotate_mask = {1, 2, 3, 0};
quadword output = __builtin_shuffle (input, rotate_mask);

(You我可能不想写得这么精确,而是想将数据重新转换为这些四字类型的数组---请参见上面的编译器资源管理器链接查看示例。)
对于x86,此宏生成的底层指令是pshufb/pshufw,(如果您不在GCC上,或者不希望具有可移植性)您可以使用_mm_shuffle_pi16内在指令(https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=shuffle&techs=MMX,SSE&IG_expand=6426)访问该指令。每个现代RISC架构都提供类似的功能。

相关问题