我知道在Java/Python等语言中,每个函数都是一个对象,因此创建许多函数需要在内存中创建它们。我也知道在Haskell中,函数被彼此缩减,所以那里几乎没有区别。
有谁知道Fortran的情况吗?使用意图/纯函数/声明较少的本地变量/其他任何内容是否有差异?
对于基于堆栈的语言(如Fortran),函数调用会带来性能成本。它们必须添加到堆栈中等等。
因此,大多数编译器将尝试积极地内联函数调用,如果可能的话。大多数情况下,编译器会在是否内联某些程序中做出正确的选择。
这个自动内联过程意味着编写函数不会产生额外的成本(完全没有成本)。
这意味着您应该尽可能清晰、有组织地编写代码,很可能编译器会为您进行这些优化。更重要的是,您解决问题的整体策略比担心函数调用的性能更有效。
尽可能以最简单和最好结构化的方式编写代码,然后在编写和测试完成后,您可以对其进行剖析以查看是否存在需要优化的热点。只有在这一点上,您才应该关注微观优化,如果您的编译器正在执行其工作,则可能不需要进行微观优化。
我刚刚花了整个上午调试一个由混合C和Fortran组成的应用程序,当然它使用了很多函数。我发现(通常也是如此)的不是函数慢,而是某些函数调用(非常少)根本就不需要执行。例如,清除内存块,只是为了整洁,但高频率地这样做。
这不是语言的功能,也不是内联的功能。函数调用可能是免费的,但您仍然会遇到调用树比必要更繁茂的问题。您需要找出在哪里进行修剪。这是我依赖的“分析”方法。
无论您做什么,都要找出需要修复的问题。不要猜测。许多人不认为这种问题是猜测,但当他们发现自己问“这会起作用吗?那会有帮助吗?”时,他们是在摸索,而不是找出问题所在。一旦他们知道问题所在,解决方案就显而易见了。
通常在Fortran中,子程序/函数调用的开销非常小。虽然语言标准没有指定参数传递机制,但典型的实现方式是“按引用传递”,因此不涉及复制,只需设置新的过程。在大多数现代架构上,这几乎没有开销。选择好的算法通常比微观优化更重要。
一个例外是当编译器必须创建临时数组时,调用可能会很快。例如,如果实际参数是非连续数组子段,并且被调用的过程参数是普通的连续数组,则会出现这种情况。假设虚拟参数是dimension(:)。使用dimension(:)的数组进行调用很简单。如果在调用中请求非单位步幅,例如array(1:12:3),则数组是非连续的,编译器可能需要创建一个临时副本。假设实际参数是dimension(:,:)。如果调用具有array(:,j),则子数组是连续的,因为在Fortran中,第一个索引在内存中变化最快,不应需要复制。但是,array(i,:)是非连续的,可能需要一个临时副本。
一些编译器具有选项,可以在需要临时数组副本时警告您,以便您可以更改代码(如果您愿意)。