어떤 함수를 inline으로 지정할까?

신연우·2023년 11월 12일
0

WIL

목록 보기
8/13

배경

백엔드 엔지니어가 코드 전반에 걸쳐 사용할 수 있는 유틸 함수를 만들고 있었습니다. 인라인 함수로 적용하고 싶어 inline 키워드를 붙였는데 아래와 같은 경고 문구가 발생했습니다.

Expected performance impact from inlining is insignificant. Inlining works best for functions with parameters of functional types

인라인 함수로 만들었을 때 예상되는 성능 향상이 거의 없을 것이라는 경고문이었습니다. 인라인 함수는 함수를 파라미터로 받는 함수에서 가장 좋은 효과를 보일 것이라고 조언까지 해줬습니다.

실제로 함수를 파라미터로 받도록 설정하니 해당 경고문이 사라졌습니다. 왜 함수를 파라미터로 받는 함수가 인라인 함수로 만들었을 때 큰 효과를 보는지 궁금해 찾아보기로 했습니다.

왜 파라미터를 함수로 받아야 할까?

함수를 인라이닝하게 되면 파라미터로 전달된 함수의 본문을 호출하는 쪽에 그대로 넣어주게 됩니다. 따라서 함수 호출을 위해 스택을 쌓고 호출이 종료되면 다시 복귀하는 동작을 하지 않아도 됩니다.

fun printMessage(lazyMessage: () -> Any?) {
    println(lazyMessage())
}

fun main() {
    printMessage { "TEMP" }
}

위와 같은 함수를 하나 정의하고 IntelliJ의 도움을 받아 java로 디컴파일 해보면 다음과 같은 코드가 나오게 됩니다.

public static final void printMessage(@NotNull Function0 lazyMessage) {
    Intrinsics.checkNotNullParameter(lazyMessage, "lazyMessage");
    Object var1 = lazyMessage.invoke();
    System.out.println(var1);
}

public static final void main() { printMessage((Function0)null.INSTANCE); }

여기서 printMessage 함수를 인라인 함수로 만들면 java 코드가 다음과 같이 변하게 됩니다.

public static final void printMessage(@NotNull Function0 lazyMessage) {
    int $i$fprintMessage = 0;
    Intrinsics.checkNotNullParameter(lazyMessage, "lazyMessage");
    Object var2 = lazyMessage.invoke();
    System.out.println(var1);
}

public static final void main() {
    int $i$fprintMessage = false;
    int var1 = false;
    String var2 = "TEMP";
    System.out.println(var2);
}

printMessage 함수를 호출하기 위해 Function0 객체를 생성하지 없어졌습니다. 이로 인해 Function0 객체 생성 비용을 없앨 수 있게 되었습니다.

결론

함수를 파라미터로 받는 함수를 반복적으로(자주) 호출하는 경우 inline 여부에 따라 성능 차이가 발생할 수 있습니다.

함수를 파라미터로 받지 않는 경우에는 함수 호출 전에 객체가 이미 생성되어 있고, 이 객체의 참조값을 넘겨주거나 값 복사를 통해 사용할 수 있기 때문에 상대적으로 얻게 되는 성능 향상이 미비하다고 경고 문구를 보여주는 것 같습니다.

참고한 글

Better Kotlin — inline

profile
남들과 함께하기 위해서는 혼자 나아갈 수 있는 힘이 있어야 한다.

0개의 댓글