如果您不介意一点 x86 内联汇编(GNU C 语法),您可以利用 supercat 的建议,在 add 后使用rotate-with-carry将完整 33 位结果的高 32 位放入寄存器.
当然,您通常应该介意使用 inline-asm,因为它会破坏一些优化(https://gcc.gnu.org/wiki/DontUseInlineAsm)。但无论如何我们都走了:
// works for 64-bit long as well on x86-64, and doesn't depend on calling convention
unsigned average(unsigned x, unsigned y)
{
unsigned result;
asm("add %[x], %[res]\n\t"
"rcr %[res]"
: [res] "=r" (result) // output
: [y] "%0"(y), // input: in the same reg as results output. Commutative with next operand
[x] "rme"(x) // input: reg, mem, or immediate
: // no clobbers. ("cc" is implicit on x86)
);
return result;
}
在我尝试的情况下,告诉编译器 args 是可交换的%
修饰符实际上并没有帮助更好的 asm,调用函数时 y 是常量或指针 deref(内存操作数)。可能对输出操作数使用匹配约束会破坏这一点,因为您不能将它与读写操作数一起使用。
正如您在 Godbolt 编译器资源管理器中看到的那样,它可以正确编译,我们将操作数更改为 的版本也是如此unsigned long
,具有相同的内联 asm。但是,clang3.9 把它弄得一团糟,并决定使用约束"m"
选项"rme"
,因此它存储到内存并使用内存操作数。
RCR-by-one 并不算太慢,但在 Skylake 上仍然是 3 uops,有 2 个周期延迟。在 RCR 具有单周期延迟的 AMD CPU 上非常棒。(来源:Agner Fog 的指令表,另请参阅x86标记 wiki 以获取 x86 性能链接)。它仍然比@sellibitze 的版本好,但比@Sheldon 的顺序依赖版本差。(参见 Godbolt 上的代码)
但请记住,inline-asm 会破坏诸如常量传播之类的优化,因此在这种情况下,任何纯 C++ 版本都会更好。