函数调用的成本是多少?
相比
- 简单的内存访问
- 磁盘存取
- 另一台计算机上的内存访问(在同一网络上)
- 另一台计算机(在同一网络上)上的磁盘访问
在Windows上的C ++中。
解决方案
取决于该函数的功能,如果它对内存中的对象进行逻辑处理,它将在列表中排第二。如果它包括磁盘/网络访问权限,则在列表的下方。
比起简单的内存访问要稍微多一点,确实可以忽略不计。
与其他所有事物相比,列出的数量要少几个数量级。
任何操作系统上的几乎任何语言都应如此。
函数调用只是将内存中的帧指针移到堆栈上,并在此之上添加一个新帧。功能参数被移入本地寄存器以供使用,并且堆栈指针前进到堆栈的新顶部以执行功能。
与时间相比
函数调用〜简单的内存访问
函数调用<磁盘访问
函数调用<另一台计算机上的内存访问
函数调用<另一台计算机上的磁盘访问
通常,函数调用的速度将比内存访问慢一些,因为它实际上必须进行多次内存访问才能执行该调用。例如,对于大多数在x86上使用__stdcall的函数调用,需要多次压入和弹出堆栈。但是,如果内存访问是什至不在L2高速缓存中的页面,那么如果目标和堆栈都在CPU的内存高速缓存中,则函数调用会更快。
对于其他所有功能,函数调用的速度要快很多(许多)。
函数调用通常只涉及几个内存副本(通常到寄存器中,因此它们不会占用太多时间),然后进行跳转操作。这将比内存访问慢,但比上述任何其他操作快,因为它们需要与其他硬件通信。通常,在任何OS /语言组合中都应如此。
如果在编译时内联函数,则函数成本等于0。
0当然是,如果没有函数调用,我们会得到什么,即:自己内联。
当我这样写的时候,这听起来当然太明显了。
难以回答,因为其中涉及许多因素。
首先,"简单内存访问"并不简单。由于以现代时钟速度运行,CPU可以比从芯片一侧到另一侧获得数字快两个数字(光速-这不只是一个好主意,这是法律)
那么,该函数是否在CPU内存缓存中被调用?我们正在比较的内存访问也是吗?
然后,我们可以通过函数调用清除CPU指令流水线,这将以不确定的方式影响速度。
实际调用该函数但不完整执行该函数的成本?或者实际执行功能的成本?仅仅设置一个函数调用并不是一项昂贵的操作(要更新PC吗?)。但是很明显,一个函数完全执行的成本取决于该函数在做什么。
假设意思是调用本身的开销,而不是被调用者可能要执行的开销,那么它肯定比除"简单"内存访问之外的所有访问都要快得多,要快得多。
它可能比内存访问慢,但是请注意,由于编译器可以进行内联,因此函数调用开销有时为零。即使不是,至少在某些体系结构上,某些对指令高速缓存中已经存在的代码的调用可能比访问主(未缓存)存储器更快。这取决于在进行调用之前需要将多少个寄存器溢出到堆栈中,以及类似的事情。请咨询编译器和调用约定文档,尽管我们不大可能比拆卸发出的代码更快地弄清它。
还要注意的是,如果操作系统必须从磁盘将页面放入,则有时不是"简单"的内存访问,因此我们将需要很长时间才能完成。如果我们跳入当前在磁盘上分页的代码,则同样如此。
如果根本的问题是"我何时应该优化代码以最大程度地减少函数调用的总数?",那么答案是"非常接近永不"。
相对计时(不应相差超过100倍;-)
- 缓存中的内存访问= 1
- 缓存中的函数调用/返回= 2
- 内存访问超出缓存= 10 .. 300
- 传输本身可以很快
- 涉及至少几千次操作,因为用户/系统阈值必须至少穿越两次;必须安排一个I / O请求,结果必须写回;可能分配了缓冲区...
- 原始传输速度可能很高,但是另一台计算机上的某些进程必须完成实际工作
函数调用的成本取决于体系结构。 x86相当慢(每个函数参数需要几个时钟加上一个时钟),而64位则要慢得多,因为大多数函数参数是在寄存器中而不是在堆栈中传递的。
函数调用实际上是将参数复制到堆栈上(多存储器访问),寄存器保存,实际代码执行,最后是结果复制和寄存器还原(寄存器保存/还原取决于系统)。
所以..相对来说:
- 函数调用>简单存储器访问。
- 函数调用<<磁盘访问-与内存相比,它的价格可能高出数百倍。
- 函数调用<<在另一台计算机上进行内存访问-网络带宽和协议是这里的主要杀手time。
- 函数调用<<<<在另一台计算机上进行磁盘访问-以上所有以及更多:)
仅内存访问比函数调用更快。
但是,如果使用内联优化的编译器(对于GCC编译器,并且不仅在使用优化的级别3(-O3)时被激活),也可以避免调用。
别忘了C ++具有虚拟调用(价格昂贵得多,大约是x10),在Windows上,我们可以期望VS进行内联调用(按定义,成本为0,因为二进制中没有调用)
该链接在Google中经常出现。供以后参考,我在Con中运行了一个简短的程序,调用了函数,而答案是:"大约是内联成本的六倍"。下面是详细信息,请参见//底部的输出。更新:为了更好地将苹果与苹果进行比较,我将Class1.Method更改为返回" void",如下所示:public void Method1(){//返回0; }
内联速度仍然快了2倍:内联(平均):610毫秒;函数调用(平均):1380毫秒。因此,更新后的答案是"大约两次"。
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用System.Text;
使用System.Diagnostics;
命名空间FunctionCallCost
{
班级计划
{
静态void Main(string [] args)
{
Debug.WriteLine(" stop1");
int iMax = 100000000; // 100M
DateTime funcCall1 = DateTime.Now;
秒表sw = Stopwatch.StartNew();
for (int i = 0; i < iMax; i++) { //gives about 5.94 seconds to do a billion loops, // or 0.594 for 100M, about 6 times faster than //the method call. } sw.Stop(); long iE = sw.ElapsedMilliseconds; Debug.WriteLine("elapsed time of main function (ms) is: " + iE.ToString()); Debug.WriteLine("stop2"); Class1 myClass1 = new Class1(); Stopwatch sw2 = Stopwatch.StartNew(); int dummyI; for (int ie = 0; ie < iMax; ie++) { dummyI = myClass1.Method1(); } sw2.Stop(); long iE2 = sw2.ElapsedMilliseconds; Debug.WriteLine("elapsed time of helper class function (ms) is: " + iE2.ToString()); Debug.WriteLine("Hi3"); } }
//这里是1级
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用System.Text;
命名空间FunctionCallCost
{
类Class1
{
public Class1() { } public int Method1 () { return 0; } }
}
// 输出:
停止1
主要功能经过的时间(毫秒)为:595
停止2
助手类功能的经过时间(毫秒)为:3780
停止1
主要功能经过的时间(毫秒)是:592
停止2
助手类功能的经过时间(毫秒)为:4042
停止1
主要功能经过的时间(毫秒)是:626
停止2
助手类功能的经过时间(毫秒)为:3755