spi*_*123 0 c assembly gcc x86-64 micro-optimization
我正在用 c 语言编写一个国际象棋引擎,速度至关重要。国际象棋引擎基于 unsigned long long,我将其表示为 u64,并且它严重依赖于最低有效位扫描。到目前为止,我一直在使用 gcc 函数 __builtin_ctzll ,它做得很好。然而,我使用 gcc -S -O2 为这个独立函数生成了汇编代码。它给了我以下内容:
xorl %eax, %eax
rep bsfq %rdi, %rax
cltq
ret
Run Code Online (Sandbox Code Playgroud)
然而经过一番调查似乎代码
rep bsfq %rdi, %rax
ret
Run Code Online (Sandbox Code Playgroud)
在我的国际象棋程序中做了完全相同的事情。然而现在速度慢了约 20%。它应该更快,因为它的指令更少。然而,原始的 __builtin_ctzll 内联在我的 C 代码中。这是我的自定义汇编代码运行速度比原始代码慢的原因吗?因为当我声明函数 ctzll 时,我当然不能在 c 中内联声明它,除非我有定义(不在汇编代码中)。
是否有另一种方法来优化汇编指令,或者我应该尝试直接在 c 中内联 asm 的新汇编代码?
两者实际上并不等同。具体来说,它们在 %rdi 为 0 的情况下有所不同。
如果输入为 0,bsf 会保留目标的先前结果,这是半定义的行为:为什么打破 LZCNT 的“输出依赖性”很重要?
这意味着 bsf 指令对其输出寄存器具有输入依赖性。将输出寄存器清零显式地打破了这种依赖性并确保在这种情况下定义了行为。在大多数现代 x86 实现中,寄存器重命名期间会发生异或清零,并破坏依赖链。这意味着异或本身实际上是免费的。这也意味着 bsf 可以被分派到执行单元,而无需等待 eax 寄存器的先前使用,这可能会导致您看到的性能差异。
然而,更有可能的是,无论您如何放入短程序集,它都对优化器隐藏了,从而迫使优化器围绕函数先前内联的位置做出次优选择。
| 归档时间: |
|
| 查看次数: |
374 次 |
| 最近记录: |