自然,我假设per Jonathon Reinhart's logic, here)运行。最近,我决定测试该假设,结果令我有些惊讶。
我知道,对于大多数现代硬件来说,这个问题纯粹是学术性的,因此必须编写循环大约10亿次的测试程序(以得到任何微小的差异,以达到更可接受的水平)。这些程序尽可能基本(以消除所有可能的干扰源)。
lt.c:
int main() {
for (int i = 0; i < 1000000001; i++);
return 0;
}
le.c:
int main() {
for (int i = 0; i <= 1000000000; i++);
return 0;
}
使用带有-std = c11标志的GCC,可以编译它们并在Linux VirtualBox 3.19.0-18-通用#18-Ubuntu x86_64安装上运行。
lt.c的二进制文件的平均时间为:
real 0m2.404s
user 0m2.389s
sys 0m0.000s
le.c的平均时间为:
real 0m2.397s
user 0m2.384s
sys 0m0.000s
差别很小,但是无论我运行二进制文件多少次,我都无法消除它或反转它。
我在lt.c的for循环中使比较值比le.c大一圈(因此它们都循环了相同的次数)。这是一个错误吗?
根据Is < faster than <=?中的答案,
<
编译为jge
,而<=
编译为jg
。那是在处理if语句而不是for循环,但这是否仍是原因? jge
的执行时间可能会比jg
的执行时间略长吗? (我认为这很具有讽刺意味,因为这意味着从C转移到ASM会使这条指令更复杂,其中C中的lt转换为ASM中的gte,而lte转换为gt。)或者,这是否仅仅是硬件专用的,以致于不同的x86线或单个芯片可能始终显示出相反的趋势,相同的趋势或没有差异?
最佳答案
在对我的问题的评论中有一些请求,要求包括GCC为我生成的程序集。在进入编译器弹出每个文件的程序集版本之后,我检查了一下。
结果:
事实证明,默认优化设置将两个for循环都转换为同一程序集。实际上,这两个文件在组装形式上都是相同的。 (diff
确认了这一点。)
先前观察到的时差的可能原因:
看来我运行二进制文件的顺序是造成运行时间差异的原因。
在给定的运行时间中,程序通常在每次连续执行时都更快地执行,大约执行3次后才达到稳定。
我在time ./lt
和time ./le
之间来回切换,所以第一次跑步会偏向于平均时间的增加。
我通常先跑。
我做了几个单独的演练(增加了平均偏差)。
代码摘录:
movl $0, -4(%rbp)
jmp .L2
.L3:
addl $1, -4($rbp)
.L2
cmpl $1000000000, -4(%rbp)
jle .L3
mol $0, %eax
pop %rbp
... *遮住脸* ...继续...
关于c - 为什么<慢于<=? [C],我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/31434458/