본문 바로가기
Kotlin

[Kotlin] inline 사용법 (1) - 왜 inline function, class, property를 사용하는 걸까?

by 노력남자 2023. 8. 13.
반응형

이번 포스팅에선 inline을 왜 사용하는지, 어떻게 사용하는지 알아보려고 한다.

 

이번 포스팅에서 전부 설명하겠지만, 이전 포스팅에 Kotlin 공식 문서에 있는 inline function, class, property 소개 글을 번역해 놓은 것이 있으니 한 번 보는 것도 좋다.

 

inline이란?

 

"Inline"이라는 용어는 프로그래밍에서 사용되는 개념으로, 주로 함수나 코드 블록을 호출하는 위치에 해당 함수 또는 코드 블록의 본문이 복사되어 실행되는 것을 의미합니다. 이렇게 하면 함수 호출의 오버헤드를 줄이고 성능을 향상시킬 수 있습니다.

일반적으로 함수를 호출하면 현재 실행 플로우는 호출된 함수로 이동하고, 해당 함수의 작업이 완료된 후에 호출한 곳으로 돌아옵니다. 이 과정에서 함수 호출의 추가적인 오버헤드가 발생할 수 있습니다.

 

inline 종류

 

왜 Inline을 사용하는지 알아보기 전에 어디에 inline을 사용하는지 알아보자.

 

inline은 

 

  • function
  • class
  • property

 

위 3 곳에 사용 가능하다.

 

각 위치 별로 왜 inline을 써야 하는지 알아보자.

 

inline function

 

- 어디에 사용할까?

 

inline function은 고차 함수(high-order functions)에 사용한다.

 

- 왜 고차 함수에 사용할까?

 

Kotlin 고차 함수는 성능상 2가지 문제가 있어 inline을 적용하면 성능상 이점을 얻을 수 있기 때문이다.

 

- Kotlin 고차 함수는 어떤 문제를 가지고 있을까?

 

exForEach라는 고차 함수를 사용하는 testInline 함수를 예를 들어 설명해보겠다.

 

아래 코드가 실제 어떻게 돌아가는지 보기 위해 자바 코드로 바꿔보겠다.

 

fun testInline() {
    val commonText = "common"
    listOf(1, 2, 3).exForEach { println(commonText) }
}

fun <T> Iterable<T>.exForEach(action: (T) -> Unit) {
    for (element in this) action(element)
}

 

자바 코드로 바꿔보면 아래와 같은데 문제점을 하나씩 설명해보겠다.

 

 

① 불필요한 함수 객체 생성

 

 

위 부분을 보면 Kotlin의 고차 함수에 사용한 람다가 Function1이라는 클래스로 변환된다. 라는 사실을 알 수 있다.

 

고차함수를 사용할 때마다 Function1 객체가 생성되기 때문에 성능상 문제가 생길 수 있다.

 

② 불필요한 변수 캡처링

 

 

람다 밖에 있는 변수를 람다 안에서 사용하면 Function1 클래스 내에서 별도 변수를 만들어 할당한다는 사실을 알 수 있다.

 

불필요한 변수 할당을 하기 때문에 성능상 문제가 생길 수 있다.

 

- 위 문제점을 inline으로 해결해보자.

 

exForEach 함수에 inline을 붙여주자. 그럼 끝이다.

 

 

자바 코드로 변환해보면 아래와 같다.

 

inline을 붙이니 exForEach를 호출하지 않고 exForEach에 있는 코드를 testInline에 아예 옮겨버렸다.

 

위에 문제점 ① 불필요한 함수 객체 생성, ② 불필요한 변수 캡처링이 사라진 걸 볼 수 있다.

 

 

- 모든 고차 함수에 적용해도 될까?

 

엄청 긴 고차 함수를 inline하면 코드 수가 증가해서 컴파일 속도가 늦어진다고는 한다.

 

그래서 어디는 3줄 어디는 5줄 이내로 작성된 것들만 사용하라고 하는데 정확하게 잘 모르겠다.

 

일단 적용해보고 정말 느리면 그때 빼도 될 거 같다는 생각이 든다.

 

inline class

 

- 어디에 사용할까?

 

value class에 사용한다.

 

- 왜 value class에 사용할까?

 

value class에 있는 value 타입으로 컴파일되게 하기 위해서 사용한다.

 

- inline이 어떻게 작동하는지 확인해보자.

 

inline class는 class 앞에 inline을 붙이지 않고 @JvmInline 어노테이션을 붙여야 한다.

 

 

value class는 inline이 필수다. 없으면 에러가 발생한다.

 

 

Money value 클래스를 예로 들어 설명하겠다.

 

예:

 

아래 Money 클래스를 사용하는 Payment 클래스를 자바 코드로 변경해보겠다.

 

data class Payment(
    val money: Money
)

@JvmInline
value class Money(val money: Long)

 

Money 클래스가 long 타입으로 변경된 걸 볼 수 있다.

 

이걸 위해 @JvmInline을 붙이는 거다.

 

 

inline property

 

- 어디에 사용할까?

 

custom property에 사용한다.

 

- 어떻게 사용할까?

 

var foo: String
    inline get() = Foo()
    inline set(v) { ... }

 

getter, setter에 각각 inline을 붙일 수 있다.

 

inline var foo: String
    get() = Foo()

 

getter, setter 둘 다에 적용시키고 싶다면 val, var 앞에 inline을 붙여주면 된다.

 

주의할 점은 백킹 필드를 쓸 수 없다.

 

 

- 왜 custom property에 사용할까?

 

아래와 같이 생긴 property를 사용하면 런타임에 어떻게 되는지 자바 코드로 변경해보겠다.

 

val String.lengthInUpperCase: Int
    get() = this.uppercase().length

fun main() {
    "노력남자".lengthInUpperCase
}

 

getLengthInUpperCase() -> this.uppercase().length 2번 호출하는 걸 볼 수 있다.

 

 

이걸 this.uppercase().length 1번만 호출하게 변경하면 얼마나 좋을까?

 

- inline을 붙여보자.

 

inline val String.lengthInUpperCase: Int
    get() = this.uppercase().length

fun main() {
    "노력남자".lengthInUpperCase
}

 

inline을 붙여주니 String.lengthInUpperCase를 호출하지 않고 바로 this.uppercase().length를 호출하는 걸 볼 수 있다.

 

반응형

댓글