两个补码的长整数

Rol*_*olf 8 assembly x86-64 micro-optimization twos-complement

我想用英特尔I64汇编程序做一些长整数数学运算(128位),需要创建一个2的补码.让我们说我的正面价值在于RDX:RAX.

2的补码是通过"翻转位并加1"来完成的.所以最天真的实现是(4条指令和14个字节的代码):

  NOT RAX
  NOT RDX
  ADD RAX,1   ; Can't use INC, it doesn't set Carry
  ADC RDX,0
Run Code Online (Sandbox Code Playgroud)

当我在RAX而不是NOT上使用NEG指令时,它对我来说是"+1"但是Carry是错误的,当RAX为零时NEG RAX清除了Carry,但是我需要携带JUST IN THIS CASE.所以下一个最好的方法可能是(4条指令和11个字节的代码):

  NOT RDX
  NEG RAX
  CMC
  ADC RDX,0                  ; fixed, thanks lurker
Run Code Online (Sandbox Code Playgroud)

还有4条说明.但是不是加+1,我可以减去-1,因为SBB将Carry-Bit加到减数上,当Carry清零时我会加+1.所以我的下一个最好的尝试是这个,有3个指令和10个字节的代码:

   NOT RDX
   NEG RAX
   SBB RDX,-1
Run Code Online (Sandbox Code Playgroud)

从我冗长的文字中可以看出,这一点并不明显.是否有一种更好,更易理解的方法来在汇编程序中进行级联2的补码?

phu*_*clv 3

较短的指令或较少的指令数量并不一定意味着更快的执行速度,因为每条指令的延迟和吞吐量都不同

\n\n

例如,像enter, dad, loop... 这样的过时指令会非常慢,并且它们的存在只是为了向后兼容。Eveninc有时比 慢addcmc与上面在某些 \xce\xbcarchs 上使用的相同

\n\n

因此,可以并行执行的较长系列的低延迟指令将运行得更快。一些常见的指令组甚至可以融合在一起形成一个宏操作。编译器的优化器总是知道这一点,并将选择最合适的指令来发出。

\n\n

对于这个片段

\n\n
__int128 negate(__int128 x)\n{\n    return -x;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

ICC 19.0.1将生成以下指令

\n\n
    xor       edx, edx                                      #3.13\n    xor       eax, eax                                      #3.13\n    sub       rax, rdi                                      #3.13\n    sbb       rdx, rsi                                      #3.13\n
Run Code Online (Sandbox Code Playgroud)\n\n

前两个异或指令的成本为零\xce\xbcop,因为它们是在寄存器重命名阶段处理的。现在你只有2 条指令要执行

\n\n

您可以在上面的 Godbolt 链接中切换编译器,以查看不同编译器(包括 MSVC)进行否定的各种方法(不幸的是它还没有 128 位类型)。以下是 GCC 和 Clang 的结果

\n\n

海湾合作委员会 8.3:

\n\n
    mov     rax, rdi\n    neg     rax\n    mov     rdx, rsi\n    adc     rdx, 0\n    neg     rdx\n
Run Code Online (Sandbox Code Playgroud)\n\n

铛:

\n\n
    mov     rax, rdi\n    xor     edx, edx\n    neg     rax\n    sbb     rdx, rsi\n
Run Code Online (Sandbox Code Playgroud)\n\n

正如您所看到的,Clang 也仅使用 3 条指令(减去第一个将数据从输入参数移动到必要目的地的指令)。但喜欢xor reg, regmov也可以“免费”

\n\n

如果您针对空间进行优化(例如在缓存未命中率较高的某些情况下),情况可能会有所不同,因为某些立即数和指令很长

\n\n

无论它是否更快,都需要一些微观基准测试。但在 Intel CPU 上,Intel 编译器 (ICC) 通常会比其他编译器实现更高的性能,因为它更好地理解架构。

\n\n

请注意,该操作称为“取反”,而不是“补码”,这是一种对负数进行编码的方法

\n


归档时间:

查看次数:

781 次

最近记录:

6 年,9 月 前