본문 바로가기
개발새발/코틀린

[코틀린/Kotlin] 인라인 함수

by 조희우 2025. 9. 1.

인라인 함수

  • 함수를 호출하는 것이 아닌 바이트 코드를 호출 위치에 붙여넣는 형식
  • 보통 람다를 인자로 넣을 경우 성능상 오버 헤드가 있으나 inline 키워드를 사용하여 람다 자체도 인라인 되어 오버헤드 감소

예시

inline fun runTwice(block: () -> Unit) {
    block()
    block()
}

사용 이유

  • 성능 최적화 고차 함수 호출 시 생성되는 람다 객체, 함수 호출 오버헤드 감소
  • 람다에서 non-local return 가능 일반적인 람다에선 return이 불가능하나 인라인 함수에서는 가능
inline fun doSomething(block: () -> Unit) {
    println("Start")
    block()
    println("End")
}

fun main() {
    doSomething {
        println("In lambda")
        return // main에서 return
    }
    println("This will NOT be printed") // 출력 안됨
}

주의 사항

  • 큰 함수나 자주 변경될 함수에는 비효율적
  • 코드의 크기가 증가됨

키워드

noinline

  • 특정 람다는 인라인 하지 않도록 제어 가능
  • 람다를 변수에 저장하거나 전달할 때 필요
inline fun wrap(block1: () -> Unit, noinline block2: () -> Unit) {
    block1() // 인라인됨
    block2() // 인라인 안됨
}

crossinline

  • non-local return을 금지하는 키워드
  • → return으로 바깥 함수 빠져나가는 걸 막음
inline fun runTask(crossinline task: () -> Unit) {
    val r = Runnable { 
        task() // 여기서 return하면 오류! 그래서 crossinline 필요
    }
    r.run()
}

고차 함수의 최적화

고차함수

함수 자체를 인수로 받거나 반환하는 함수

fun doSomthing(action: () -> Unit) {
	action()
}

고차 함수의 문제점

  • 람다 객체 생성
  • 외부 변수 참조때 추가 객체가 필요
  • 간접 호출 비용 발생

→ 짧고 자주 호출되는 함수일 수록 성능 비용 과다 발생!

inline함수 사용

inline fun doSomthing(action: () -> Unit) {
	action()
}
  • 컴파일 타임에 함수 호출부에 함수 본문이 직접 삽입
    → 람다도 복사됨!
    → 람다 객체가 생성되지 않음
    → 간접 호출도 제거됨!

주의점

  • inline을 많이 사용하면 코드의 크기가 커짐 → binary size 증가
  • 람다 블럭이 큰 경우에는 오히려 비효율적
  • 재귀 함수, suspend 함수 등 일부 상황에서는 inline이 불가능하거나 제한적임
    • 재귀함수: 인라인 함수는 호출된 위치에 코드가 복사되므로 자기 자신을 복사하는 꼴이 되어 무한 루프
    • suspend: inline함수는 JVM 바이트 코드로 인라인될 수 있어야하나, suspend 함수는 코루틴 상태 머신으로 변환되어 인라인과 충돌
    • Non-local return 제한: noinline에서는 사용이 불가능
  •  

 

문제

1. 다음 조건을 만족하는 measureTime 인라인 함수를 구현하세요.

[조건]

  • measureTime 함수는 인자로 고차 함수 block: () -> T 를 받습니다.
  • block 함수를 실행하기 전과 후의 시간을 측정해, 소요 시간을 밀리초 단위로 출력합니다.
  • 최종적으로 block()의 리턴값을 반환합니다.
  • 함수는 inline으로 선언되어야 합니다.

[사용 예시]

fun main() {
    val result = measureTime {
        Thread.sleep(500)  // 0.5초 대기
        "Done!"
    }
    println("Result: $result")
}


[출력 예시]

Time: 500ms
Result: Done!



답:

inline fun <T> measureTime(block: () -> T): T {
    val start = System.currentTimeMillis()
    val result = block()
    val end = System.currentTimeMillis()
    println("${end - start}ms")
    return result
}


보충: return이 String인 경우

inline fun measureTime(block: () -> Unit): String {
    val start = System.currentTimeMillis()
    block()
    val end = System.currentTimeMillis()
    return "${end - start}ms"
}