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的补码?
较短的指令或较少的指令数量并不一定意味着更快的执行速度,因为每条指令的延迟和吞吐量都不同
\n\n例如,像enter, dad, loop... 这样的过时指令会非常慢,并且它们的存在只是为了向后兼容。Eveninc有时比 慢add。cmc与上面在某些 \xce\xbcarchs 上使用的相同
因此,可以并行执行的较长系列的低延迟指令将运行得更快。一些常见的指令组甚至可以融合在一起形成一个宏操作。编译器的优化器总是知道这一点,并将选择最合适的指令来发出。
\n\n对于这个片段
\n\n__int128 negate(__int128 x)\n{\n return -x;\n}\nRun Code Online (Sandbox Code Playgroud)\n\nICC 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\nRun 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\nRun Code Online (Sandbox Code Playgroud)\n\n铛:
\n\n mov rax, rdi\n xor edx, edx\n neg rax\n sbb rdx, rsi\nRun Code Online (Sandbox Code Playgroud)\n\n正如您所看到的,Clang 也仅使用 3 条指令(减去第一个将数据从输入参数移动到必要目的地的指令)。但喜欢xor reg, reg,mov也可以“免费”
如果您针对空间进行优化(例如在缓存未命中率较高的某些情况下),情况可能会有所不同,因为某些立即数和指令很长
\n\n无论它是否更快,都需要一些微观基准测试。但在 Intel CPU 上,Intel 编译器 (ICC) 通常会比其他编译器实现更高的性能,因为它更好地理解架构。
\n\n请注意,该操作称为“取反”,而不是“补码”,这是一种对负数进行编码的方法
\n| 归档时间: |
|
| 查看次数: |
781 次 |
| 最近记录: |