我正在运行下面的代码,基本上做的很少。它只增加2和4亿次并输出运行时。
#include "time.h"
#include <iostream>
using namespace std;
void add (){
int tot = 2+4;
}
void main(){
int t = clock();
int count = 0;
while(count<100000000){
int tot = 2+4;
count++;
}
cout <<endl<< "runtime = " << fixed <<(double) (clock() - t) / CLOCKS_PER_SEC <<"s" << endl;
}
但我有兴趣看到完成相同但调用函数时的时差。所以我用“add()”替换了“int tot = 2 + 4”这一行。
我原本期望第二个运行时间略长,但时间要长得多。第一次实施= .3s
,第二次实施= 3s
。
我理解调用该函数需要使用堆栈来存储返回地址并存储本地数据。但它必须做很多更多呢?
如果有人可以向我解释究竟是什么导致了运行时间的巨大差异,或者我正在做一些愚蠢的事情,那将会很棒。
答案 0 :(得分:5)
正如Seth已经提到的,内联函数可能会得到优化。
在第一种情况下(基本优化)最有可能而不是不断添加这两个数字,它将解决2 + 4到6而只是做一个简单的
mov eax, 6 ;eax is just an example here or
mov tot_addr, 6 ; mem->mem mov
在第二种情况下,因为它是一个函数调用,系统必须
push 4 ;assuming 4 byte ints
push 4 ;wrote 2 to be clear that you have to push both variables
call add
或沿着这些方向的东西。因为需要为该函数创建调用堆栈(为了简单起见,省略了返回值push等)。一旦该函数返回,堆栈指针需要在第一次推送之前移回,然后RET将设置指令指针。正如您所看到的,这比做一个简单的
更昂贵mov eax, 4
add eax, 2
如果你只做一个简单的(非优化的添加)
,可能就是这种情况编辑:这里有一些关于内嵌函数的更多信息。当你内联函数时,它只需要函数本身可以执行的任何功能,并将指令直接放在引用的位置,而不是执行CALL指令并直接设置函数调用。例如,而不是
mov eax, 4
mov ecx, 2
push 4 ; size for return value
push eax
push ecx
call add
你最终会得到
mov eax, 4
mov exc, 2
add eax, ecx
在代码中:
int a = 4;
int b = 2;
int res = add(a, b);
会变成
int a = 4;
int b = 2;
int res = a + b;
假设您内联添加功能。
答案 1 :(得分:2)
相对于做其他事情,函数调用非常昂贵(比你想象的要贵),正如你提到的那样。尝试声明您的函数inline
和/或启用优化以恢复性能。
答案 2 :(得分:0)
函数调用确实可以为CPU做更多的工作,而不仅仅是按下返回地址和参数。
在某些CPU上,你会遇到管道停滞。在阅读下一条指令时,甚至可能会出现i-cache错误。这些很容易造成你所注意到的10倍减速。
这就是为什么编译器会竭尽全力优化函数调用。