相比
- 简单的内存访问
- 磁盘访问
- 另一台计算机上的内存访问(在同一网络上)
- 另一台计算机上的磁盘访问(在同一网络上)
在 Windows 上的 C++ 中。
相比
在 Windows 上的 C++ 中。
相对时间(不应该超过 100 倍 ;-)
函数调用只是将内存中的帧指针转移到堆栈上,并在其上添加一个新帧。函数参数被移入本地寄存器以供使用,堆栈指针前进到堆栈的新顶部以执行函数。
与时间相比
函数调用~简单的内存访问
函数调用<磁盘访问
函数调用<另一台计算机上的内存访问函数调用<另一台计算机
上的磁盘访问
与简单的内存访问相比 - 稍微多一点,真的可以忽略不计。
与列出的其他所有内容相比 - 数量级少。
这应该适用于任何操作系统上的几乎任何语言。
一般来说,函数调用会比内存访问稍慢,因为它实际上必须进行多次内存访问才能执行调用。例如,在 x86 上使用 __stdcall 的大多数函数调用都需要多次推送和弹出堆栈。但是,如果您的内存访问甚至不在 L2 缓存中的页面,如果目标和堆栈都在 CPU 的内存缓存中,则函数调用会快得多。
对于其他一切,函数调用要快很多(很多)数量级。
很难回答,因为涉及的因素很多。
首先,“简单内存访问”并不简单。由于在现代时钟速度下,CPU 可以将两个数字相加,而不是从芯片的一侧到另一侧获得一个数字(光速——这不仅仅是一个好主意,它是定律)
那么,函数是在 CPU 内存缓存中调用的吗?您是否也在比较它的内存访问?
然后我们有函数调用将清除 CPU 指令流水线,这将以非确定性的方式影响速度。
假设您的意思是调用本身的开销,而不是被调用者可能会做的事情,那么它肯定比“简单”内存访问要快得多。
它可能比内存访问慢,但请注意,由于编译器可以进行内联,因此函数调用开销有时为零。即使不是,至少在某些架构上,对指令缓存中已经存在的代码的某些调用可能比访问主(未缓存)内存更快。这取决于在进行调用之前需要将多少寄存器溢出到堆栈,等等。请查阅您的编译器和调用约定文档,尽管您不太可能比反汇编发出的代码更快地弄清楚它。
另请注意,有时不是“简单”的内存访问 - 如果操作系统必须从磁盘导入页面,那么您将等待很长时间。如果您跳转到当前在磁盘上分页的代码,情况也是如此。
如果根本的问题是“我什么时候应该优化我的代码以最小化函数调用的总数?”,那么答案是“非常接近永远”。
这个链接在谷歌中出现了很多。为了将来参考,我在 C# 中运行了一个关于函数调用成本的简短程序,答案是:“大约是内联成本的六倍”。以下是详细信息,请参阅底部的//输出。更新:为了更好地比较苹果和苹果,我将 Class1.Method 更改为 return 'void',如下所示: public void Method1 () { // return 0; 尽管如此,内联速度还是快了 2 倍:内联(平均)
:610 毫秒;函数调用(平均):1380 毫秒。因此,更新后的答案是“大约两倍”。
使用系统;使用 System.Collections.Generic;使用 System.Linq;使用 System.Text;使用 System.Diagnostics;
命名空间 FunctionCallCost { 类程序 { 静态无效 Main(string[] args) { Debug.WriteLine("stop1"); int iMax = 100000000; //100M 日期时间 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");
}
}
// 这里使用 System 1 类;使用 System.Collections.Generic;使用 System.Linq;使用 System.Text;
命名空间 FunctionCallCost { 类 Class1 {
public Class1()
{
}
public int Method1 ()
{
return 0;
}
}
}
// 输出:stop1 main 函数的运行时间(ms)是:595 stop2 helper 类函数的运行时间(ms)是:3780
stop1 main 函数的运行时间(ms)是:592 stop2 helper 类函数的运行时间(ms)是:4042
stop1 main 函数的运行时间(ms)是:626 stop2 helper 类函数的运行时间(ms)是:3755
实际调用函数但没有完全执行它的成本?还是实际执行该功能的成本?简单地设置一个函数调用并不是一项昂贵的操作(更新 PC?)。但显然,一个函数完全执行的成本取决于该函数在做什么。
我们不要忘记 C++ 有虚拟调用(明显更贵,大约 x10),在 WIndows 上,您可以期望 VS 内联调用(定义为 0 成本,因为二进制文件中没有剩余调用)
取决于该函数的作用,如果它对内存中的对象执行逻辑,它将在您的列表中排名第二。如果它包括磁盘/网络访问,则在列表的下方。
一个函数调用通常只涉及几个内存副本(通常是到寄存器中,因此它们不应该占用太多时间),然后是一个跳转操作。这将比内存访问慢,但比上面提到的任何其他操作都快,因为它们需要与其他硬件进行通信。在任何操作系统/语言组合上通常都应如此。
如果函数在编译时内联,则函数的成本等于 0。
0 当然是,如果没有函数调用,你会得到什么,即:自己内联它。
当我这样写时,这当然听起来太明显了。
函数调用的成本取决于架构。x86 相当慢(几个时钟加上每个函数参数一个时钟左右),而 64 位则要慢得多,因为大多数函数参数是在寄存器中传递的,而不是在堆栈中传递的。
函数调用实际上是将参数复制到堆栈上(多次内存访问),寄存器保存,实际代码执行,最后是结果复制和寄存器恢复(寄存器保存/恢复取决于系统)。
所以..相对来说:
只有内存访问比函数调用快。
但是,如果编译器具有内联优化(对于 GCC 编译器,并且不仅在使用优化级别 3 (-O3) 时激活),则可以避免调用。