视频先行
扫码看视频吧!
然后下面的就不用看了
或者……你喜欢看文字?
Kotlin 里有个特别好用的关键字叫 inline,它可以帮你对做了标记的函数进行内联优化。所谓内联就是,调用的函数在编译的时候会变成代码内嵌的形式:
这样的好处很明显,调用栈变浅了嘛,对吧?
不过事实上这种对调用栈的优化的效果非常小,小到了应该被忽略的程度。是应该被忽略,不是可以被忽略,因为这种优化不仅没啥用,而且还可能因为代码多处拷贝而导致编译生成的字节码膨胀,从而变成负优化。所以,这种东西我们要它干嘛呢?
——哎?那我讲个屁呀?
正文在这里
大家好,我是扔物线朱凯。
Java 里有个概念叫编译时常量 Compile-time Constant,直观地讲就是这个变量的值是固定不变的,并且编译器在编译的时候就能确定这个变量的值。具体到代码上,就是这个变量需要是 final 的,类型只能是字符串或者基本类型,而且这个变量需要在声明的时候就赋值,等号右边还不能太复杂。总之就是你得让编译器一眼瞟过去就能看出结果。这种编译时常量,会被编译器以内联的形式进行编译,也就是直接把你的值拿过去替换掉调用处的变量名来编译。这样一来,程序结构就变简单了,编译器和 JVM 也方便做各种优化。这,就是编译时常量的作用。
这种编译时常量,到了 Kotlin 里有了一个专有的关键字,叫 const:一个变量如果以 const val 开头,它就会被编译器当做编译时常量来进行内联式编译:
——当然你得符合编译时常量的特征啊,不然会报错,不给编。
inline
让变量内联用的是 const;而除了变量,Kotlin 还增加了对函数进行内联的支持。在 Kotlin 里,你给一个函数加上 inline 关键字,这个函数就会被以内联的方式进行编译。但!虽然同为内联,inline 关键字的作用和目的跟 const 是完全不同的。
编译时常量为什么这么多限制?因为只有符合这些限制,编译器和 JVM 才有能力做优化,从而这种内联操作也才有意义。稍微复杂一点,就优化不动了。什么叫「稍微复杂」我不知道,但是函数内联这种操作,绝对算得上是相当复杂了,绝对优化不动的。其实真要较真起来,函数的内联确实会产生一种被动的优化,就是刚才我说的:去掉一个函数,调用栈少了一层,性能的损耗肯定会少一些,但实际上调用栈本身所造成的性能损耗本来就是非常小的,这个优化跟没优化差不多。这个事实可能不太符合我们的直觉,但你这样想一下:在我们看到的各种性能优化规范里,你有没有见过类似「少写几个方法来减少调用栈」这样的优化策略?没有吧?为什么?因为这种优化没有意义。而同时,函数内联不同于常量内联的地方在于,函数体通常比常量复杂多了,而函数内联会导致函数体被拷贝到每个调用处,如果函数体比较大而被调用处又比较多,就会导致编译出的字节码变大很多。我们都知道编译结果的压缩是应用优化的一大指标,而函数内联对于这项指标是明显不利的。所以靠 inline 来做性能优化?不存在的。
那么问题就来了:inli