我在使用Google基准测试对函数进行优化时进行了尝试,但在某些情况下我的代码出现了意外的减速。我开始进行实验,并查看编译后的汇编代码,最终创建了一个展示该问题的最小化测试案例。以下是我创建的展示这种减速情况的汇编代码:
.text
test:
#xorps %xmm0, %xmm0
cvtsi2ss %edi, %xmm0
addss %xmm0, %xmm0
addss %xmm0, %xmm0
addss %xmm0, %xmm0
addss %xmm0, %xmm0
addss %xmm0, %xmm0
addss %xmm0, %xmm0
addss %xmm0, %xmm0
addss %xmm0, %xmm0
retq
.global test
该函数遵循GCC/Clang的x86-64函数声明调用规范extern "C" float test(int);
注意已注释的xorps
指令。取消注释此指令会大幅提高函数的性能。在使用i7-8700K的计算机上进行测试,Google基准测试显示不带xorps
指令的函数需要8.54纳秒(CPU),而带有xorps
指令的函数只需要1.48纳秒。我已经在多台计算机上进行了测试,包括各种操作系统、处理器、处理器代数和不同的处理器制造商(英特尔和AMD),它们都呈现出类似的性能差异。重复执行addss
指令会使减速更加明显(到一定程度),并且即使在这里使用其他指令(例如mulss
)或甚至混合指令,只要它们都以某种方式依赖于%xmm0
中的值,这种减速仍然会发生。值得指出的是,仅在每个函数调用中调用xorps
会导致性能改善。在循环中对性能进行抽样(如Google Benchmark所示)并将xorps
调用放在循环外部仍然显示较慢的性能。
由于这是“仅添加”指令可以提高性能的情况,因此似乎是CPU中非常底层的东西导致的。由于它在各种CPU上都会出现,因此看起来这必须是有意为之的。但是,我找不到任何解释为什么会发生这种情况的文档。有人对这里发生的事情有解释吗?问题似乎取决于复杂因素,因为我在原始代码中看到的减速只发生在特定的优化级别(-O2,有时是-O1,但不是-Os),没有内联,并且使用了特定的编译器(Clang,但不是GCC)。