当我所做的只是将循环代码移动到函数中时,代码运行速度会慢10倍

Code runs ten times slower when all I did was move the loop code into a function

本文关键字:代码 运行 速度 10倍 函数 代码移动 循环      更新时间:2023-10-16

我正在运行下面的代码,它基本上做的很少。它只是将2和4相加1亿次,然后输出运行时间。

#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;

}

但我很感兴趣的是,当做完全相同的事情,但调用一个函数时,看到的时间差异。因此,我将"int tot = 2+4"一行替换为"add()"。

我原以为第二次运行时间会稍微长一点,但实际上长得多。第一次实现= .3s,第二次实现= 3s

我理解调用函数需要使用堆栈来存储返回地址和存储本地数据。但是它一定做了很多更多的然后呢?

如果有人能向我解释到底是什么导致了运行时的巨大差异,或者也许我在做一些愚蠢的事情,那就太好了。

正如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等)。一旦这个函数返回,堆栈指针需要被移回第一次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;

假设您内联了add函数

相对于做其他事情,函数调用是非常昂贵的(比您想象的要昂贵),原因正是您提到的。尝试声明您的函数inline和/或启用优化以恢复性能。

函数调用确实会给CPU带来比简单地推送返回地址和参数更多的工作。

在某些cpu上,您将获得管道停滞。甚至可能在读取下一条指令时i-cache丢失。这些很容易导致比你注意到的10倍更大的减速。

这就是为什么编译器会不遗余力地优化函数调用