이 글은 Effective Java 를 완독하고, Kotlin 을 상용으로 사용하는 개발자 입장에서
Effective Kotlin 글 중 새로운 내용, remind 할 필요 있는 부분, 핵심 내용 등만 추려 정리한 내용입니다.
#
inline modifier 를 붙여주면 컴파일 단계에서 호출하는 부분을 inline 이 정의된 구현으로 변경해준다.
이 동작은 아래의 장점을 가진다.
- type argument 가 reified 될 수 있다.
- 더 빠른 수행이 된다.
- non-local return 이 허용된다.
물론 단점도 있는데 이는 나중에 알아본다.
A type argument can be reified
#
Java 가 처음부터 generic 을 가지고 있던 게 아니었다.
J2SE 5.0 버전과 함께 2004년에 generic 이 추가되었다.
그리고 generic type 은 컴파일 때 제거가 되서 JVM byte code 에는 그 정보가 없다.
예를 들어 List 는 List 로 컴파일되며, 그렇기 때문에 type 이 List 인지 체크가 안 되는 것이다. 오직 List 인지만 체크 가능한 것이다.
any is List // Error
any is List<*> // OK
같은 이유로 type argument 에 대한 operation 도 불가능하다.
fun printTypeName(){
print(T::class.simpleName) // Error
}
#
inline 과 reified modifier 를 사용함으로써 type erase 를 피할 수 있다. inline 되기 때문이다.
inline fun printTypeName(){
print(T::class.simpleName)
}
Functions with functional parameters are faster when they are inlined
#
약간이지만 jump 동작이 없고, object creation 도 없기 때문에 약간의 성능 향상이 있다.
functional parameter 는 compile 할 때 anonymous 로 compile 된다. (object creation)
function type 자체가 first-class citizen 이 아니기 때문이다. (JS 는 first-class citizen)
그러므로 functional parameter 없이 inline 하는 것은 실질적 성능 향상이 크지 않기 때문에 IDE 가 warning 을 준다.
#
local 변수 를 참조하는 경우 퍼포만스 차이가 크게 생길 수 있다.
var a = 2L
noinlineRepeat(100_000_000){
a += a / 2
}
local 변수는 non-inlines lambda 에서 바로 접근 가능하지 않으므로 compile 타임에 아래와 같이 변경된다.
val a = Ref.LongRef()
a.element = 2L
noinlineRepeat(100_000_000){
a.element += a.element / 2
}
이럴 경우 성능차이는 뚜렷해진다.
#
사소한 차이더라도 누적되면 큰 차이가 되므로, 특히 stdlib 이나 Util 에서 제공하는 자주 불리는 함수일 경우 특히 주의해야 한다.
Non-local return is allowed
Const of inline modifier
#
inline function 은 제한적인 visibility 를 가진 element 를 사용할 수 없다.
public inline function 에서 private 이나 internal function, property 를 사용할 수 없다.
#
반복적 사용시 code 량이 상당히 증가한다는 단점도 있다.
Crossinline and noinline
#
function type argument 가 crossinline 또는 noinline 으로 마킹된 경우 inline function 에 제약이 가해진다.
crossinline 은 inline 은 될 수 있지만, non-local return 을 지원하지 않는 경우를 명시한다. 이는 보통 해당 function type argument 이 다른 scope 에서 사용되는 경우(예를 들면 inline 되지 않은 다른 lambda 에서 사용됨)에 사용한다.
noinline 은 해당 argument 가 inline 되어서는 안 된다는 것을 명시한다. 이는 inline 되지 않은 다른 함수로 해당 argument 를 전달할 때 사용한다.
inline fun requestNewToken(
hasToken: Boolean,
corssinline onRefresh: ()->Unit,
noninline onGenerate: ()->Unit
){
if(hasToken){
httpCall("get-token", onGenerate) // no inline func 에 전달하므로 onGenerate 는 noinline
}else{
httpCall("refresh-token"){
onRefresh() // 다른 lambda 에서 사용되어 non-local return 을 허용하지 않으므로 crossinline
onGenerate()
}
}
}
fun httpCall(url:String, callback :()->Unit){ /../ }
Summary
끝
댓글