为什么调用函数时会有开销?
Why is there overhead when calling functions?
通常,人们谈到在程序中调用函数会产生一定量的开销,或者一组不可避免的额外问题和情况。这可以更好地解释并与没有函数调用的类似程序进行比较吗?
函数可以是inlined
,但规范(大部分)是函数位于特定地址,传递给函数的值放在堆栈上,然后将结果放在堆栈上,然后返回。
另请参阅 here and here 以了解有关何时使用内联的讨论。
内联
一般情况下,您只能建议编译器使用 inline
a function, but the compiler might decide otherwise. Visual Studio offers its own forceinline
关键字。有些函数不能内联,例如当它们是递归的或当目标函数不能在编译时确定时(通过函数表调用,C++ 中的虚函数调用)。
我建议你相信编译器是否应该内联一个函数。如果您真的想内联代码,请考虑改用宏。
开销
当你使用函数时,内存开销是最小的,因为你没有重复代码;内联代码被复制到调用站点。现在的性能开销可以忽略不计,因为现代架构确实很好的预测和调用,只有大约 1-2 个周期的开销。
这取决于您的编译器设置及其优化代码的方式。一些函数是内联的。其他人不是。这通常取决于您是针对大小还是针对速度进行优化。
一般来说,调用函数会导致延迟的原因有两个:
程序需要挂钩到内存中函数代码开始的某个随机位置。为此,它需要将当前光标位置保存到堆栈中,以便它知道 return 的位置。此过程消耗超过一个 CPU 周期。
根据您的 CPU 体系结构,可能有一个管道,它在您当前的指令执行的同时从内存中提取下几条指令到 CPU 缓存中。这是为了加快执行速度。当您调用一个函数时,游标会挂钩到一个完全不同的地址,并且所有缓存的指令都会从管道中清除。这会导致进一步延迟。
函数当然可以内联,如果满足某些条件,但它们肯定不会总是内联。大多数情况下,调用函数会产生真正的非内联函数调用。一个函数调用有一些额外的开销,比如
- 正在按照函数的调用约定为函数准备参数
- 接收函数的return值
- 函数序言和尾声代码,负责本地内存管理、参数内存管理和寄存器值保存
- 函数可以破坏一些 CPU 寄存器,从而破坏它们在调用代码中的使用,从而阻碍优化
- 更少 CPU 以非线性方式执行的代码的缓存友好和虚拟内存友好行为
如果将函数体内联嵌入到调用代码中,所有这些都会产生的开销很可能不存在。
通常,人们谈到在程序中调用函数会产生一定量的开销,或者一组不可避免的额外问题和情况。这可以更好地解释并与没有函数调用的类似程序进行比较吗?
函数可以是inlined
,但规范(大部分)是函数位于特定地址,传递给函数的值放在堆栈上,然后将结果放在堆栈上,然后返回。
另请参阅 here and here 以了解有关何时使用内联的讨论。
内联
一般情况下,您只能建议编译器使用
inline
a function, but the compiler might decide otherwise. Visual Studio offers its ownforceinline
关键字。有些函数不能内联,例如当它们是递归的或当目标函数不能在编译时确定时(通过函数表调用,C++ 中的虚函数调用)。我建议你相信编译器是否应该内联一个函数。如果您真的想内联代码,请考虑改用宏。
开销
当你使用函数时,内存开销是最小的,因为你没有重复代码;内联代码被复制到调用站点。现在的性能开销可以忽略不计,因为现代架构确实很好的预测和调用,只有大约 1-2 个周期的开销。
这取决于您的编译器设置及其优化代码的方式。一些函数是内联的。其他人不是。这通常取决于您是针对大小还是针对速度进行优化。
一般来说,调用函数会导致延迟的原因有两个:
程序需要挂钩到内存中函数代码开始的某个随机位置。为此,它需要将当前光标位置保存到堆栈中,以便它知道 return 的位置。此过程消耗超过一个 CPU 周期。
根据您的 CPU 体系结构,可能有一个管道,它在您当前的指令执行的同时从内存中提取下几条指令到 CPU 缓存中。这是为了加快执行速度。当您调用一个函数时,游标会挂钩到一个完全不同的地址,并且所有缓存的指令都会从管道中清除。这会导致进一步延迟。
函数当然可以内联,如果满足某些条件,但它们肯定不会总是内联。大多数情况下,调用函数会产生真正的非内联函数调用。一个函数调用有一些额外的开销,比如
- 正在按照函数的调用约定为函数准备参数
- 接收函数的return值
- 函数序言和尾声代码,负责本地内存管理、参数内存管理和寄存器值保存
- 函数可以破坏一些 CPU 寄存器,从而破坏它们在调用代码中的使用,从而阻碍优化
- 更少 CPU 以非线性方式执行的代码的缓存友好和虚拟内存友好行为
如果将函数体内联嵌入到调用代码中,所有这些都会产生的开销很可能不存在。