我最近看到Util
我朋友写的一个类有这么多inline
方法。这门课是关于图像的,并且有类似的方法
public static inline function resize ( img:Image, width:Int, height:Int)
{
//... method implementation goes here.
}
我问他为什么要使用inline
. 我知道编译器会改变
var img:Image = ImgUtil.resize(img, 100, 100);
至
var img:Image = // Implementation of method here
他只是说它减少了对函数的调用,并且只在实用方法上使用它们。每次复制相同的代码不会增加程序大小吗?
现在需要这个吗?如果需要,什么时候应该使用它们?
编辑
我在 Patrick 提供的链接中列出的列表中列出了最多的问题和优势,以帮助不想通过链接的未来观众。
问题
用扩展的函数体替换调用站点可能会以多种方式降低性能:
- 在代码大小比速度更重要的应用程序中,例如许多嵌入式系统,内联通常是不利的,除了非常小的函数,例如琐碎的 mutator 方法。
- 代码大小的增加可能会导致一小部分关键代码部分不再适合缓存,从而导致缓存未命中和速度减慢。
- 内联过程中添加的变量可能会消耗额外的寄存器,并且在寄存器压力已经很高的区域中,这可能会强制溢出,从而导致额外的 RAM 访问。
- 语言规范可能允许程序对过程的参数做出额外的假设,这些假设在过程被内联后不能再做出。
- 如果代码大小增加太多,可能会超出 RAM 大小等资源限制,从而导致程序无法运行或导致抖动。今天,除了非常激进的内联之外,这不太可能成为台式机或服务器计算机的问题,但它仍然可能是嵌入式系统的问题。
通常,编译器开发人员会牢记这些问题,并在他们的编译器中加入启发式方法,在大多数情况下选择内联哪些函数以提高而不是恶化性能。
优点:
内联扩展本身是一种优化,因为它消除了调用的开销,但它作为一种启用转换更为重要。也就是说,一旦编译器在其调用站点的上下文中扩展了一个函数体——通常带有可能是固定常量的参数——它可能能够进行以前不可能的各种转换。例如,在这个特定的调用站点,条件分支可能总是为真或总是为假。这进而可以实现死代码消除、循环不变代码运动或归纳变量消除。