컴파일 시간 개선을 위한 팁
컴파일 시간 개선을 위한 팁
Kotlin/Native 컴파일러는 성능을 개선하는 업데이트를 꾸준히 받고 있어요. 최신 Kotlin/Native 컴파일러와 제대로 구성된 빌드 환경을 쓰면 Kotlin/Native 타깃을 가진 프로젝트의 컴파일 시간을 크게 줄일 수 있습니다.
Kotlin/Native 컴파일 과정을 빠르게 하는 팁을 소개할게요.
본문
일반 권장 사항
최신 버전의 Kotlin 사용하기
이렇게 하면 항상 최신 성능 개선 사항을 얻을 수 있어요. 가장 최근 Kotlin 버전은 2.4.20입니다.
거대한 클래스 만들지 않기
컴파일하는 데 오래 걸리고 실행 중 로드에 시간이 걸리는 거대한 클래스는 피하세요.
다운로드·캐시된 구성 요소를 빌드 사이에 보존하기
프로젝트를 컴파일하면 Kotlin/Native는 필요한 구성 요소를 다운로드하고 작업 결과 일부를 $USER_HOME/.konan 디렉터리에 캐시해요. 컴파일러는 이후 컴파일에 이 디렉터리를 사용해서 완료 시간을 줄입니다.
컨테이너(예: Docker)나 CI(지속적 통합) 시스템에서 빌드하면 컴파일러가 빌드할 때마다 ~/.konan 디렉터리를 처음부터 만들어야 할 수 있어요. 이 단계를 피하려면 빌드 사이에 ~/.konan이 보존되도록 환경을 구성하세요. 예를 들어 konan.data.dir Gradle 프로퍼티로 그 위치를 다시 정의할 수 있습니다.
또는 -Xkonan-data-dir 컴파일러 옵션으로 cinterop과 konanc 도구를 통해 디렉터리의 커스텀 경로를 구성할 수 있어요.
Gradle 구성
Gradle로 첫 컴파일은 의존성 다운로드, 빌드 캐시 구축, 추가 단계 수행 때문에 보통 이후 컴파일보다 시간이 더 걸립니다. 실제 컴파일 시간을 정확히 측정하려면 프로젝트를 최소 두 번 이상 빌드해야 해요.
아래는 더 나은 컴파일 성능을 위한 Gradle 구성 권장 사항입니다.
Gradle 힙 크기 늘리기
Gradle 힙 크기를 늘리려면 gradle.properties 파일에 org.gradle.jvmargs=-Xmx3g를 추가하세요.
병렬 빌드를 사용한다면 org.gradle.workers.max 프로퍼티나 --max-workers 커맨드 라인 옵션으로 적절한 워커 수를 골라야 할 수 있어요. 기본값은 CPU 프로세서 수입니다.
필요한 바이너리만 빌드하기
build나 assemble처럼 프로젝트 전체를 빌드하는 Gradle 태스크는 정말 필요할 때만 실행하세요. 이 태스크들은 같은 코드를 두 번 이상 빌드해서 컴파일 시간을 늘립니다. IntelliJ IDEA에서 테스트를 실행하거나 Xcode에서 앱을 시작하는 같은 일반적인 경우에는 Kotlin 도구가 불필요한 태스크 실행을 피합니다.
비전형적인 경우나 빌드 구성이라면 태스크를 직접 골라야 할 수 있어요:
linkDebug*: 개발 중에 코드를 실행하려면 보통 바이너리 하나만 필요하므로, 그에 해당하는linkDebug*태스크를 실행하는 것으로 충분합니다.embedAndSignAppleFrameworkForXcode: iOS 시뮬레이터와 디바이스는 프로세서 아키텍처가 다르기 때문에 Kotlin/Native 바이너리를 유니버설(fat) 프레임워크로 배포하는 것이 일반적이에요. 하지만 로컬 개발 중에는 사용 중인 플랫폼용으로만.framework파일을 빌드하는 것이 더 빠릅니다. 플랫폼별 프레임워크를 빌드하려면 embedAndSignAppleFrameworkForXcode(프레임워크를 프로젝트에 연결) 태스크를 사용하세요.
필요한 타깃에서만 빌드하기
위 권장 사항과 비슷하게, 모든 네이티브 플랫폼용 바이너리를 한 번에 빌드하지 마세요. 예를 들어 *XCFramework 태스크로 XCFramework 컴파일은 모든 타깃에 대해 같은 코드를 빌드하므로, 단일 타깃만 빌드하는 것보다 비례해서 시간이 더 걸립니다.
설정에 XCFramework가 꼭 필요하다면 타깃 수를 줄일 수 있어요. 예를 들어 인텔 Mac의 iOS 시뮬레이터에서 프로젝트를 실행하지 않는다면 iosX64는 필요 없습니다.
서로 다른 타깃의 바이너리는
linkDebug*$Target과linkRelease*$TargetGradle 태스크로 빌드됩니다. 빌드 로그에서 실행된 태스크를 찾거나,--scan옵션으로 Gradle 빌드를 실행해서 Gradle 빌드 스캔에서 확인할 수 있어요.
불필요한 릴리스 바이너리 빌드하지 않기
Kotlin/Native는 debug와 release(바이너리 선언) 두 가지 빌드 모드를 지원해요. release는 고도로 최적화되며 이로 인해 시간이 많이 걸립니다. release 바이너리 컴파일은 debug 바이너리보다 한 자릿수 더 오래 걸립니다.
실제 릴리스가 아니라면 이런 최적화들은 일반적인 개발 주기에서 불필요할 수 있어요. 개발 과정에서 이름에 Release가 들어간 태스크를 사용한다면 Debug로 바꾸는 것을 고려해 보세요. 마찬가지로 assembleXCFramework 대신 예를 들어 assembleSharedDebugXCFramework를 실행할 수 있습니다.
release 바이너리는
linkRelease*Gradle 태스크로 빌드됩니다.--scan옵션으로 Gradle 빌드를 실행해 빌드 로그나 Gradle 빌드 스캔에서 확인할 수 있어요.
release 바이너리용 캐시 활성화하기
기본적으로 Kotlin/Native는 링크 타임 최적화(LTO) 모드에서 release 바이너리를 컴파일합니다. 모든 모듈을 함께 컴파일하고 최적화하죠. 이렇게 하면 release 바이너리가 런타임에 더 빠르지만 컴파일 시간이 크게 늘어납니다.
컴파일러 최적화보다 빠른 컴파일을 우선하고 싶다면 release 모드에서 캐싱을 활성화할 수 있어요. 캐싱을 활성화하려면 gradle.properties 파일에 다음 두 옵션을 모두 추가하세요:
# Enables the compiler to use caches in release mode
kotlin.native.binary.enableReleaseBinaryCache=true
# Makes the Kotlin Gradle plugin invoke the compiler with `-Xauto-cache-from` and related options
kotlin.internal.native.enableReleaseBinaryCache=true
이 기능은 활발히 개발 중이므로 런타임 성능에는 아직 개선 여지가 있어요. 성능 개선은 향후 Kotlin 릴리스에서 계획되어 있습니다.
release 바이너리 크기 줄이기
release 바이너리 크기를 줄이고 빌드 시간을 개선하려면 이진 옵션(활성화 방법) smallBinary를 활성화해 보세요.
이 옵션은 LLVM 컴파일 단계에서 컴파일러의 기본 최적화 인자로 -Oz를 효과적으로 설정해요. 이 옵션은 아직 실험적(stability)이며 일부 경우 런타임 성능에 영향을 줄 수 있습니다.
전이적 export 사용하지 않기
transitiveExport = true(바이너리에 의존성 export)를 사용하면 많은 경우 죽은 코드 제거(dead code elimination)가 비활성화되어 컴파일러가 쓸모없는 코드를 많이 처리해야 합니다. 이로 인해 컴파일 시간이 늘어나요. 대신 필요한 프로젝트와 의존성을 export할 때는 export 메서드를 명시적으로 사용하세요.
모듈을 과도하게 export하지 않기
불필요한 모듈 export(바이너리에 의존성 export)는 피하세요. export하는 각 모듈은 컴파일 시간과 바이너리 크기에 부정적인 영향을 줍니다.
Gradle 빌드 캐싱 사용하기
Gradle 빌드 캐시 기능을 활성화하세요:
- 로컬 빌드 캐시: 로컬 캐싱을 위해
gradle.properties파일에org.gradle.caching=true를 추가하거나 커맨드 라인에서--build-cache옵션으로 빌드를 실행합니다. - 원격 빌드 캐시: 지속적 통합 환경을 위한 원격 빌드 캐시 구성 방법을 배워 보세요.
Gradle 구성 캐시 사용하기
Gradle 구성 캐시는 구성 단계 결과를 캐싱해 빌드 성능을 개선합니다. 또한 단일 프로젝트 내에서 독립 태스크의 병렬 실행을 가능하게 하고 org.gradle.parallel 프로퍼티를 암시적으로 활성화해 서로 다른 프로젝트의 태스크가 병렬로 실행되게 합니다.
Gradle 구성 캐시를 사용하려면 gradle.properties 파일에 org.gradle.configuration-cache=true 프로퍼티를 추가하세요.
구성 캐시는
link*태스크를 병렬로 실행하는 것도 가능하게 하는데, 특히 CPU 코어가 많으면 머신에 부하가 걸릴 수 있어요. 이 문제는 KT-70915에서 해결될 예정입니다.
이전에 비활성화했던 기능 활성화하기
과거에 빌드 문제를 우회하려고 일부 Kotlin/Native 기능을 비활성화했을 수 있어요. 예를 들면:
kotlin.native.disableCompilerDaemon=true는 Gradle 데몬을 비활성화합니다.disableNativeCache는 컴파일 캐시를 비활성화합니다(binaries 참조).
원래 우회가 필요했던 문제가 이미 해결되었을 수 있어요. gradle.properties 파일이나 Gradle 빌드 파일에 이런 줄이 있다면 제거하고 빌드가 성공하는지 확인하세요.
klib 아티팩트의 증분 컴파일 시도하기
증분 컴파일에서는 프로젝트 모듈이 생성한 klib 아티팩트의 일부만 바뀌면 klib의 일부만 다시 바이너리로 컴파일됩니다.
이 기능은 베타(stability) 상태예요. 활성화하려면 gradle.properties 파일에 다음 옵션을 추가하세요:
kotlin.incremental.native=true
문제가 생기면 YouTrack에 이슈를 만들어 주세요.
Windows 구성
Windows 보안(Windows Security)이 Kotlin/Native 컴파일러를 느리게 만들 수 있어요. 기본적으로 %USERPROFILE%에 위치한 .konan 디렉터리를 Windows 보안 예외에 추가하면 이 문제를 피할 수 있습니다. Windows 보안에 예외 추가하는 방법을 배워 보세요.
LLVM 구성
위 팁들로 컴파일 시간이 개선되지 않았다면 LLVM 백엔드(커스터마이징 방법)를 조정하는 것을 고려해 보세요.