반응형
8.2 인라인 함수 : 람다의 부가 비용 없애기
- 람다를 사용하는 구현은 똑같은 작업을 수행하는 일반 함수를 사용한 구현보다 덜 효율적이다
- 람다가 변수를 포획하면 람다 생성때마다 새로운 무명 클래스 객체 생성
- 반복되는 코드를 별도의 함수로 빼내기 + 효율적인 코드는 없을까? --> inline 함수!
8.2.1 인라이닝이 작동하는 방식
- inline 변경자를 함수에 붙이면 컴파일러는 그 함수를 호출하는 모든 문장을 함수 본문에 해당하는 바이트 코드로 바꿔준다
inline fun <T> synchronized(lock: Lock, action: () -> T): T{
lock.lock()
try{
return action()
}
finally {
lock.unlock()
}
}
val l = Lock()
synchronized(l){
//~
}
- 다중 스레스 환경에서 공유 자원의 동시접근을 막기위한 코드
- 위 코드의 synchronized는 Lock 클래스의 인스턴스를 요구한다
- java에서는 임의의 객체에 대해 synchronized를 사용할 수 있다
- 위 함수를 inline으로 선언했으므로 synchronized를 호출하는 코드는 모두 자바의 synchronized문과 같아진다
fun foo(l : Lock){
println("Before sync")
synchronized(l){
println("Action")
}
println("After sync")
}
- 위와 같이 코드를 작성하면 아래 코드와 동등한 코드가 된다
fun __foo__(l: Lock){
println("Before sync")
l.lock()
try{
println("Action")
}finally{
l.unlock()
}
println("After sync")
}
- synchronized 함수의 본문 뿐 아니라, synchronized에 전달된 람다의 본문도 함께 inline 된다
- 람다의 본문에 의해 만들어지는 바이트코드는 그 람다를 호출하는 코드 정의의 일부분으로 간주되기 때문에 코틀린 컴파일러는 그 람다를 함수 인터페이스를 구현하는 무명클래스로 감싸지 않는다
class LockOwner(val lock : Lock){
fun runUnderLock(body : () -> Unit){
synchronized(lock, body)
}
}
- 위와 같은 경우에는 synchronized 함수를 호출할때 body의 람다 코드를 알수없기 때문에 synchronized 함수의 본문만 inline 된다
8.2.2 인라인 함수의 한계
- 함수 inline 되면 그 함수에 전달된 람다식의 본문은 결과 코드에 직접 들어갈 수 있다
- 람다가 본문에 직접 펼쳐지기 때문에 파라미터로 받은 함수를 본문에 사용하는 방식이 한정된다
- 파라미터로 받은 람다를 다른 변수에 저장하고, 그 변수를 나중에 사용한다면 이 람다를 인라이닝 할 수 없다
fun <T,R> Sequence<T>.map(transform : (T) -> R):Sequence<R>{
return TransformingSequence(this, transform)
}
- 위와 같은 경우, transform에 람다식을 받아서 Sequence를 변환시켜서 반환해주는 코드
- transform 파라미터로 전달받은 함수값을 호출하지 않고, TransformingSequence 클래스의 생성자에 함수 값을 넘긴다
- 이런 경우에는 transform을 inline 시킬수없다. 함수 인터페이스를 구현하는 무명클래스 인스턴스로 만들어야만 한다
8.2.3 컬렉션 연산 인라이닝
- kotlin의 filter, map 함수는 인라인 함수
- filter, map 은 결과를 저장하는 중간 리스트들을 만든다 (부가비용이 커진다)
- Sequence를 사용하면 중간리스트를 사용하는데 드는 부가비용은 줄어든다
- Sequence는 람다를 저장해야 하므로 람다를 inline하지 않는다
- 그래서 오히려 크기가 작은 collection은 성능이 나쁠수도있다
- 크기가 큰 collection -> Sequence 사용
- 크기가 작은 collection -> inline 사용한 일반 컬렉션 사용이 나을수도 있다
8.2.4 함수를 인라인으로 선언해야 하는 경우
- inline 키워드를 사용해도 람다를 인자로 받는 함수만 성능이 좋아질 가능성이 높다
- 일반 함수 호출의 경우 JVM은 이미 강력하게 인라이닝을 지원한다
- JVM은 코드 실행을 분석해서 가장 이익이 되는 방향으로 호출을 인라이닝 한다
- 람다를 인자로 받는 함수를 인라이닝하면 이익이 많다
- 함수 호출비용 감소
- 람다를 표현하는 클래스, 람다에 해당하는 객체 만드는 비용 감소
- 코드 크기자체가 너무 크면 inline 지양
8.2.5 자원 관리를 위해 인라인된 람다 사용
- 앞서본 synchronized 처럼 자원 관리를 위한 메소드들이 있다
- withLock
- synchronized와 같은 기능
- use
- 자바의 try-with-resource와 같은 기능
- withLock
반응형
'Languages' 카테고리의 다른 글
kotlin의 제네릭 타입 파라미터 (generic, property) (0) | 2020.06.03 |
---|---|
kotlin 고차함수에서의 흐름제어 (return, @label) (0) | 2020.05.27 |
kotlin의 고차함수 (lambda) (0) | 2020.05.26 |
kotlin의 구조분해 선언과 component 함수 (0) | 2020.05.18 |
kotlin 의 비교 연산자 오버로딩 (equals, compareTo) (0) | 2020.05.18 |