以下所有说明都做同样的事情:设置%eax为零.哪种方式最佳(需要最少的机器周期)?
xorl %eax, %eax
mov $0, %eax
andl $0, %eax
Run Code Online (Sandbox Code Playgroud) Haswell及更早版本的ADC通常为2 uops,有2个周期延迟,因为Intel uops传统上只能有2个输入(https://agner.org/optimize/).在Haswell为FMA引入3输入微指令和某些情况下的索引寻址模式的微融合之后,Broadwell/Skylake及其后来都有单uop ADC/SBB/CMOV .
(但不适用于adc al, imm8短格式编码,或其他al/ax/eax/rax,imm8/16/32/32短格式,没有ModRM.我的答案中有更详细的说明.)
但是adc,即时0是特殊的Haswell解码为只有一个uop. @BeeOnRope测试了这个,并在他的uarch-bench中包含了对这个性能怪癖的检查:https://github.com/travisdowns/uarch-bench.从输出样本CI一个的Haswell服务器上示出之间的差adc reg,0和adc reg,1或adc reg,zeroed-reg.
(对于SBB也是如此.就我所见,在任何CPU上具有相同立即数的等效编码,ADC和SBB性能之间从来没有任何差别.)
这个优化何时adc bl,0推出?
我测试了Core 2 1,发现imm=0延迟是2个周期,相同adc eax,0.同时,也是循环计数是与吞吐量测试一些变化相同的adc eax,3对比0,所以第一代的Core 2(Conroe处理器/ Merom处理器)并没有这样做优化.
回答这个问题的最简单方法可能是在Sandybridge系统上使用我的测试程序,看看是否3比它快adc eax,0.但基于可靠文档的答案也可以.
(顺便说一句,如果有人可以访问Sandybridge上的perf计数器,你还可以通过运行@ BeeOnRope的测试代码来清除在执行uop计数不是处理器宽度倍数的循环时性能降低的谜团.或者是性能我在不再工作的SnB上观察到的只是因为未分层与正常的uops有什么不同?)
脚注1:我在运行Linux的Core 2 E6600(Conroe/Merom)上使用了这个测试程序.
;; NASM / YASM
;; assemble / link this …Run Code Online (Sandbox Code Playgroud) 我开发了一个小基准。该基准测试中循环的结果应转换为零,并且下一轮计算应取决于之前循环的零“结果”来测量代码的延迟而不是其吞吐量。将结果移至另一个寄存器并对其本身进行异或运算是行不通的,因为当今的 CPU 认识到与其自身进行异或运算并不依赖于之前的指令。所以我尝试从寄存器本身中减去寄存器,希望CPU(Ryzen Threadripper 3990X)没有像XOR这样的快捷方式。我用一个单独的程序对此进行了评估:
#include <iostream>
#include <chrono>
using namespace std;
using namespace chrono;
int main()
{
auto start = high_resolution_clock::now();
for( size_t i = 1'000'000'000; i--; )
__asm
{
sub eax, eax
sub eax, eax
sub eax, eax
sub eax, eax
sub eax, eax
sub eax, eax
sub eax, eax
sub eax, eax
sub eax, eax
sub eax, eax
}
double ns = (int64_t)duration_cast<nanoseconds>( high_resolution_clock::now() - start ).count() / 10'000'000'000.0;
cout << ns << endl;
}
Run Code Online (Sandbox Code Playgroud)
“不幸的是”CPU在这里也做了一个捷径,每条指令大约需要0.06ns,即。CPU 在每个时钟周期 …