就我(有限的)优化知识而言,我一直认为编译器优化并不重要。我的意思是,我们肯定可以通过使用寄存器而不是内存和展开循环来获得相当多的时间(可能是 0...100% 作为一个非常粗略的初步想法),但限制代码性能的基本因素确实是算法的选择。
我最近开始了一个宠物项目 - 一种小型解释脚本语言。它编译成字节码并使用虚拟机执行。为了便于调试、分析和测试,我首先自然地用(and ) 的-O0 -g
标志编译了代码,然后用. 然后我在虚拟机上运行了一个小程序,它基本上是这样做的(伪代码,在项目公开之前我不会向你展示实际的语法):gcc
clang
-O2
time
i = 1
sum = 0
while (i < 10000000) {
sum = (sum + i * i) mod 1000000
i++
}
print sum
这大致转换为以下伪程序集:
load r0, 0 # sum = 0
load r1, 1 # i = 1
load r2, 1000000
load r3, 10000000
loop:
mul r4, r1, r1 # i * i
add r0, r0, r4 # sum += i * i
mod r0, r0, r2 # sum %= 1000000
inc r1
gt r5, r1, r3 # if i > 10000000
jz r5, loop # then don't goto loop
ret r0
所以基本上,这是一个有 10000000 次迭代的紧密循环。time
报告它在编译时运行 0.47...0.52 秒-O2
,在编译时运行 1.51...1.74 秒,在启用-O0 -g
分析 () 时运行 3.16...3.47 秒。-pg
如您所见,最快和最慢的执行时间之间存在 7 倍的差异。
这本身并不令人惊讶,因为我知道额外的调试信息和缺乏小的优化确实会使代码运行速度变慢,但现在是有趣的部分。为了更真实地了解实际发生的情况,我使用 Lua 5.2 玩过同样的游戏。摆弄Makefile
,我发现同样的 Lua 程序:
local sum = 0
for i = 1, 10000000 do
sum = (sum + i * i) % 1000000
end
print(sum)
使用 编译 Lua 时运行大约 0.8...0.87 秒,启用-O0 -g -pg
only 时运行时间为 0.39...0.43 秒。-O2
因此,当优化器对其执行棘手的修复时,我的代码似乎有 7 倍的速度提升,而 Lua 参考实现似乎从这些改进中受益要少得多。
现在我的问题是:
知道为什么会这样吗?我怀疑主要原因是 Lua 的创建者比我更聪明,并且更了解编译器和他们的 VM 在做什么。
我也发现自己在想“这一定是过早的优化;让我把它传递给优化器,无论如何它都会完成这项工作”几次。这些包括在几乎每条VM指令的实现中调用的单行静态函数(我认为无论如何都会在必要时内联它们),使用各种复杂的表达式(有时具有不太容易预测的副作用)不过,这可以简化。我的这种态度也算吗?(顺便说一句,这是正确的态度吗?)
无论如何,我应该担心这种现象吗?毕竟,我的代码运行速度比 Lua 慢 1.5 倍——这非常好(至少对我来说已经足够好了)。我是否应该仅仅因为不这样做表明我对自己的代码缺乏深入的了解而尝试提高调试构建的性能?或者我可以完全忘记它,只要发布(优化)构建足够快?
(如果这个问题更适合 Prog.SE 或 CodeReview,请告诉我,我会迁移它。)