본문 바로가기
프로그래밍 놀이터/Kotlin, Coroutine

[Effective Kotlin] Item 46 : User inline modifier for functions with parameters of functional types

by 돼지왕 왕돼지 2022. 6. 7.
반응형

이 글은 Effective Java 를 완독하고, Kotlin 을 상용으로 사용하는 개발자 입장에서
Effective Kotlin 글 중 새로운 내용, remind 할 필요 있는 부분, 핵심 내용 등만 추려 정리한 내용입니다.

 

#
inline modifier 를 붙여주면 컴파일 단계에서 호출하는 부분을 inline 이 정의된 구현으로 변경해준다.
이 동작은 아래의 장점을 가진다.

  1. type argument 가 reified 될 수 있다.
  2. 더 빠른 수행이 된다.
  3. 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

 

 

 

반응형

댓글