기준 프로필

기준 프로필은 Android 런타임(ART)이 설치 중에 중요한 경로를 기계어 코드로 사전 컴파일하는 데 사용하는 APK에 포함된 클래스 및 메서드 목록입니다. 기준 프로필은 앱이 시작을 최적화하고, 버벅거림을 줄이고, 최종 사용자가 경험하는 성능을 개선할 수 있도록 해 주는 일종의 프로필 기반 최적화(PGO)입니다.

기준 프로필 작동 방식

프로필 규칙은 assets/dexopt/baseline.prof에서 APK의 바이너리 형식으로 컴파일됩니다.

설치가 진행될 때 ART가 프로필에서 메서드의 AOT(Ahead-of-time) 컴파일을 실행하여 메서드의 실행 속도를 높입니다. 프로필이 앱 실행 시 또는 프레임 렌더링 중에 사용되는 메서드를 포함하는 경우, 사용자가 경험하는 실행 시간이 빨라지고 버벅거림이 줄어듭니다.

앱 또는 라이브러리를 개발할 때, 시작, 전환, 스크롤과 같이 렌더링 시간이나 지연 시간이 중요성을 갖는 중요한 사용자 여정의 특정 주요 경로를 포괄하는 기준 프로필을 정의하여 사용할 수 있습니다.

그러면 기준 프로필은 Google Play를 통해 APK와 함께 사용자에게 직접 제공됩니다(그림 1 참고).

그림 1. 이 다이어그램은 업로드부터 최종 사용자에게 제공하기까지의 기준 프로필 워크플로와 이 워크플로가 클라우드 프로필과 갖는 관계를 보여줍니다.

기준 프로필을 사용해야 하는 이유

시작 시간은 애플리케이션의 사용자 참여를 높여 주는 중요한 구성요소입니다. 앱의 속도와 반응성을 높이면 일일 활성 사용자를 늘리고 평균 재방문율을 높일 수 있습니다.

클라우드 프로필도 이러한 상호작용을 최적화하긴 하나, 업데이트가 출시된 시점에서 하루 이상이 지난 후에 사용자에게 제공되며 Android 7(API 24)~Android 8(API 26)을 지원하지 않는다는 단점이 있습니다.

여러 Android 버전의 컴파일 동작

Android 플랫폼은 버전별로 서로 다른 앱 컴파일 방식을 사용하며, 그에 따라 버전별로 성능에 미치는 영향이 다릅니다. 기준 프로필은 모든 설치를 위한 프로필을 제공함으로써 이전의 컴파일 방식을 개선합니다.

Android 버전 컴파일 방법 최적화 방식
Android 5(API 수준 21)~Android 6(API 수준 23) 완전한 AOT 설치 중에 앱 전체가 최적화됩니다. 따라서 앱을 사용하기까지의 대기 시간이 길어지고, RAM 및 디스크 공간 사용량이 늘어나며, 디스크에서 코드를 로드하는 데 걸리는 시간이 길어져서 콜드 스타트 시간이 늘어납니다.
Android 7(API 수준 24)~Android 8.1(API 수준 27) 부분적인 AOT(기준 프로필) 최초 실행 시에 앱 모듈이 이 종속 항목을 정의할 때 androidx.profileinstaller에 의해 기준 프로필이 설치됩니다. ART가 앱이 사용되는 동안 추가 프로필 규칙을 추가하고 기기가 유휴 상태일 때 규칙을 컴파일하여 이를 한층 개선할 수 있습니다. 이로 인해 디스크로부터 코드를 로드하는 데 걸리는 시간과 디스크 공간이 최적화되어 앱의 대기 시간이 줄어듭니다.
Android 9(API 수준 28) 및 이후 버전 부분적인 AOT(기준 프로필 + 클라우드 프로필) Play가 앱 설치 중에 기준 프로필을 사용하여 APK 및 클라우드 프로필(있는 경우)을 최적화합니다. 설치 후에는 ART 프로필이 Play에 업로드되고 집계되며, 다른 사용자들이 앱을 설치/업데이트할 때 클라우드 프로필로 제공됩니다.

기준 프로필 만들기

BaselineProfileRule을 사용하여 자동으로 프로필 규칙 만들기

앱 개발자는 Jetpack Macrobenchmark 라이브러리를 사용하여 자동으로 각 앱 출시의 프로필을 생성할 수 있습니다.

Macrobenchmark 라이브러리를 사용하여 기준 프로필을 만들려면 다음 단계를 따르세요.

  1. 앱의 build.gradle에서 ProfileInstaller 라이브러리에 종속 항목을 추가하여 로컬 및 Play 스토어 기준 프로필 컴파일을 사용 설정합니다. 이는 로컬에서 기준 프로필을 사이드로드하는 유일한 방법입니다.

    dependencies {
         implementation("androidx.profileinstaller:profileinstaller:1.2.0-beta01")
    }
    
  2. Gradle 프로젝트에서 Macrobenchmark 모듈을 설정합니다.

  3. BaselineProfileGenerator라는 이름의 다음과 같은 새 테스트를 정의합니다.

    @ExperimentalBaselineProfilesApi
    @RunWith(AndroidJUnit4::class)
    class BaselineProfileGenerator {
        @get:Rule val baselineProfileRule = BaselineProfileRule()
    
        @Test
        fun startup() =
            baselineProfileRule.collectBaselineProfile(packageName = "com.example.app") {
                pressHome()
                // This block defines the app's critical user journey. Here we are interested in
                // optimizing for app startup. But you can also navigate and scroll
                // through your most important UI.
                startActivityAndWait()
            }
    }
    
  4. Android 9 및 이후 버전을 실행하는 userdebug 또는 루팅된 Android 오픈소스 프로젝트(AOSP) 에뮬레이터를 연결합니다.

  5. 터미널에서 adb root 명령어를 실행하여 adb 데몬이 루트 권한으로 실행되고 있는지 확인합니다.

  6. 테스트를 실행하고 완료될 때까지 기다립니다.

  7. logcat에서 생성된 프로필 위치를 찾습니다. 로그 태그 Benchmark를 검색합니다.

    com.example.app D/Benchmark: Usable output directory: /storage/emulated/0/Android/media/com.example.app
    
    # List the output baseline profile
    ls /storage/emulated/0/Android/media/com.example.app
    SampleStartupBenchmark_startup-baseline-prof.txt
    
  8. 생성된 파일을 기기에서 가져옵니다.

    adb pull storage/emulated/0/Android/media/com.example.app/SampleStartupBenchmark_startup-baseline-prof.txt .
    
  9. 생성된 파일의 이름을 baseline-prof.txt로 변경하고 앱 모듈의 src/main 디렉터리에 복사합니다.

수동으로 프로필 규칙 정의하기

src/main 디렉터리에 baseline-prof.txt 파일을 만들어서 앱 또는 라이브러리 모듈에서 수동으로 프로필 규칙을 정의할 수 있습니다. 이 폴더는 AndroidManifest.xml 파일을 포함하는 동일한 폴더입니다.

파일은 한 줄에 하나의 규칙을 정의합니다. 각 규칙은 최적화할 앱 또는 라이브러리의 메서드 또는 클래스를 매칭하기 위한 패턴을 나타냅니다.

규칙의 문법은 adb shell profman --dump-classes-and-methods를 사용할 때 사람이 읽을 수 있는 ART 프로필 형식의 상위 집합입니다. 문법은 설명어 및 서명 문법과 매우 비슷하며, 규칙 작성 프로세스의 간소화를 위해 와일드 카드도 허용합니다.

다음 예에서는 Jetpack Compose 라이브러리에 포함된 몇 가지 기준 프로필 규칙을 보여줍니다.

HSPLandroidx/compose/runtime/ComposerImpl;->updateValue(Ljava/lang/Object;)V
HSPLandroidx/compose/runtime/ComposerImpl;->updatedNodeCount(I)I
HLandroidx/compose/runtime/ComposerImpl;->validateNodeExpected()V
PLandroidx/compose/runtime/CompositionImpl;->applyChanges()V
HLandroidx/compose/runtime/ComposerKt;->findLocation(Ljava/util/List;I)I
Landroidx/compose/runtime/ComposerImpl;

규칙 문법

규칙의 형식은 메서드 또는 클래스를 타겟팅하는 두 가지 형식 중 하나입니다.

[FLAGS][CLASS_DESCRIPTOR]->[METHOD_SIGNATURE]

클래스 규칙은 다음 패턴을 사용합니다.

[CLASS_DESCRIPTOR]
문법 설명
FLAGS H, S, P 문자 중 하나 이상을 나타내며, 시작 유형과 관련하여 이 메서드가 Hot, Startup, Post Startup 중 무엇으로 플래그되어야 하는지 나타냅니다.

플래그가 H인 메서드는 앱의 수명 주기 중에 여러 차례 호출되는 '핫' 메서드입니다.

플래그가 S인 메서드는 시작 시에 호출되는 메서드입니다.

플래그가 P인 메서드는 시작과 관련 없는 핫 메서드입니다.

이 파일에 있는 클래스는 시작 중에 사용된다는 사실을 나타내며, 클래스 로드 비용을 발생시키지 않으려면 클래스를 힙에 미리 할당해야 합니다. ART 컴파일러는 메서드의 AOT 컴파일과 같은 여러 최적화 전략을 사용하며, 생성된 AOT 파일에서 레이아웃을 최적화합니다.
CLASS_DESCRIPTOR 타겟팅된 메서드의 클래스 설명어입니다. 예를 들어, androidx.compose.runtime.SlotTable의 설명어는 Landroidx/compose/runtime/SlotTable;입니다. 참고: 여기서 L은 DEX(Dalvik Executable) 형식에 따라 앞에 추가되었습니다.
METHOD_SIGNATURE 이름, 매개변수 유형, 메서드의 반환 유형을 포함하는 메서드의 서명입니다. 예를 들어, LayoutNode의 메서드

// LayoutNode.kt

fun isPlaced():Boolean {
// ...
}

는 서명 isPlaced()Z를 갖습니다.

이러한 패턴에는 단일 규칙이 여러 메서드나 클래스를 포괄하도록 와일드 카드가 포함될 수 있습니다. Android 스튜디오에서 규칙 문법을 작성할 때 도움이 필요한 경우 Android 기준 프로필 플러그인을 살펴보세요.

다음은 와일드 카드 규칙의 예입니다.

HSPLandroidx/compose/ui/layout/**->**(**)**

기준 프로필 규칙에서 지원되는 유형

기준 프로필 규칙은 다음 유형을 지원합니다. 이러한 유형에 관한 자세한 내용은 DEX(Dalvik Executable) 형식을 참고하세요.

문자 유형 설명
B byte 부호 있는 바이트
C char UTF-16으로 인코딩된 유니코드 문자 코드 포인트
D double 배정밀도 부동 소수점 값
F float 단일 정밀도 부동 소수점 값
I int 정수
J long long 정수
S short 부호 있는 short
V void void
Z boolean true 또는 false
L(클래스 이름) reference 클래스 이름의 인스턴스

라이브러리는 AAR 아티팩트에 패키징될 규칙을 정의할 수도 있습니다. 이러한 아티팩트를 포함하도록 APK를 빌드하는 경우 (매니페스트가 병합되는 방식과 비슷하게) 규칙이 함께 병합되고 이 APK에만 적용되는 간단한 바이너리 ART 프로필로 컴파일됩니다.

ProfileInstaller를 사용하는 경우 Android 9(API 수준 28) 또는 Android 7(API 수준 24)에서 설치 시점에 애플리케이션의 특정 하위 집합을 AOT 컴파일하는 데 기기에서 APK가 사용될 때 APK가 이 프로필을 사용합니다.

추가 참고사항

기준 프로필을 만들 때 다음 사항에 유의하세요.

  • Android 5~Android 6(API 수준 21 및 23)은 이미 설치 시점에 APK를 AOT 컴파일합니다.

  • 디버그 가능한 애플리케이션은 문제 해결을 위해 AOT 컴파일되지 않습니다.

  • 규칙 파일의 이름은 baseline-prof.txt여야 하고 기본 소스 세트의 루트 디렉터리에 있어야 합니다(AndroidManifest.xml 파일의 동위 파일이어야 함).

  • 이러한 파일은 Android Gradle 플러그인 7.1.0-alpha05 이상(Android Studio Bumblebee Canary 5)을 사용하는 경우에만 사용됩니다.

  • 현재 Bazel은 기준 프로필을 읽어서 APK로 병합하는 기능을 지원하지 않습니다.

  • 기준 프로필은 압축 후 1.5MB를 초과해서는 안 됩니다. 따라서 라이브러리와 애플리케이션은 영향을 극대화하는 소수의 프로필 규칙을 정의하기 위해 노력해야 합니다.

  • 애플리케이션의 너무 많은 부분을 컴파일하는 광범위한 규칙은 디스크 액세스 횟수가 늘기 때문에 시작 속도를 느리게 만들 수 있습니다. 반드시 기준 프로필의 성능을 테스트하세요.

개선사항 측정하기

Macrobenchmark 라이브러리를 사용하여 측정 자동화하기

Macrobenchmark를 사용하면 CompilationMode API를 통해 BaselineProfile 사용량을 비롯한 측정 전 컴파일을 제어할 수 있습니다.

BaselineProfileRule 테스트를 Macrobenchmark 모듈에서 설정해 두었다면 이 모듈에서 새 테스트를 정의하여 성능을 평가할 수 있습니다.

@RunWith(AndroidJUnit4::class)
class BaselineProfileBenchmark {
  @get:Rule
  val benchmarkRule = MacrobenchmarkRule()

  @Test
  fun startupNoCompilation() {
    startup(CompilationMode.None())
  }

  @Test
  fun startupBaselineProfile() {
    startup(CompilationMode.Partial(
      baselineProfileMode = BaselineProfileMode.Require
    ))
  }

  private fun startup(compilationMode: CompilationMode) {
    benchmarkRule.measureRepeated(
      packageName = "com.example.app",
      metrics = listOf(StartupTimingMetric()),
      iterations = 10,
      startupMode = StartupMode.COLD,
      compilationMode = compilationMode
    ) { // this = MacrobenchmarkScope
        pressHome()
        startActivityAndWait()
    }
  }
}

테스트 결과를 전달하는 예는 그림 2에 나와 있습니다.

그림 2. 규모가 작은 테스트의 결과입니다. 앱이 클수록 기준 프로필에서 더 많은 이익을 얻을 수 있습니다.

위 예에서는 StartupTimingMetric을 살펴보고 있지만, 그 밖에도 Jetpack Macrobenchmark를 사용하여 측정할 수 있는 Jank(프레임 측정항목)와 같은 여러 중요한 측정항목이 있습니다.

수동으로 앱 개선사항 측정하기

먼저 참고를 위해 최적화되지 않은 앱 시작을 측정해 보겠습니다.

PACKAGE_NAME=com.example.app
# Force Stop App
adb shell am force-stop $PACKAGE_NAME
# Reset compiled state
adb shell cmd package compile --reset $PACKAGE_NAME
# Measure App startup
# This corresponds to `Time to initial display` metric
# For additional info https://developer.android.com/topic/performance/vitals/launch-time#time-initial
adb shell am start-activity -W -n $PACKAGE_NAME/.ExampleActivity \
 | grep "TotalTime"

다음으로, 기준 프로필을 사이드로드합니다.

# Unzip the Release APK first
unzip release.apk
# Create a ZIP archive
# Note: The name should match the name of the APK
# Note: Copy baseline.prof{m} and rename it to primary.prof{m}
cp assets/dexopt/baseline.prof primary.prof
cp assets/dexopt/baseline.profm primary.profm
# Create an archive
zip -r release.dm primary.prof primary.profm
# Confirm that release.dm only contains the two profile files:
unzip -l release.dm
# Archive:  release.dm
#   Length      Date    Time    Name
# ---------  ---------- -----   ----
#      3885  1980-12-31 17:01   primary.prof
#      1024  1980-12-31 17:01   primary.profm
# ---------                     -------
#                               2 files
# Install APK + Profile together
adb install-multiple release.apk release.dm

설치 시에 패키지가 최적화되었는지 확인하기 위해 다음 명령어를 실행합니다.

# Check dexopt state
adb shell dumpsys package dexopt | grep -A 1 $PACKAGE_NAME

출력에 패키지가 컴파일되었다는 내용이 명시됩니다.

[com.example.app]
  path: /data/app/~~YvNxUxuP2e5xA6EGtM5i9A==/com.example.app-zQ0tkJN8tDrEZXTlrDUSBg==/base.apk
  arm64: [status=speed-profile] [reason=install-dm]

이제 앞에서와 동일한 방식으로, 단 이번에는 컴파일된 상태를 재설정하지 않고 앱 시작 성능을 측정하겠습니다.

# Force Stop App
adb shell am force-stop $PACKAGE_NAME
# Measure App startup
adb shell am start-activity -W -n $PACKAGE_NAME/.ExampleActivity \
 | grep "TotalTime"

알려진 문제

기준 프로필에는 현재 몇 가지 알려진 문제가 있습니다.

  • App Bundle에서 APK를 빌드할 때 기준 프로필이 올바르게 패키징되지 않습니다. 이 문제를 해결하려면 com.android.tools.build:gradle:7.3.0-beta02 및 이후 버전을 적용하세요(문제).

  • 기준 프로필은 기본 classes.dex 파일에 대해서만 올바르게 패키징됩니다. 이는 .dex 파일이 둘 이상인 앱에 영향을 미칩니다. 이 문제를 해결하려면 com.android.tools.build:gradle:7.3.0-beta02 및 이후 버전을 적용하세요(문제).

  • Macrobenchmark는 Android 12L(API 32)(문제) 및 Android 13(API 33)(문제)에서 의 기준 프로필과 호환되지 않습니다.

  • user(루팅되지 않은) 빌드에서는 ART 프로필 캐시의 재설정이 허용되지 않습니다. 이 문제를 해결하기 위해 androidx.benchmark:benchmark-macro-junit4:1.1.0-rc02에는 벤치마크 중에 앱을 재설치하는 수정사항이 포함되어 있습니다(문제).

  • Android 스튜디오 프로파일러는 앱을 프로파일링할 때 기준 프로필을 설치하지 않습니다(문제).

  • Gradle 이외의 빌드 시스템(Bazel, Buck 등)은 기준 프로필을 출력 APK로 컴파일하는 기능을 지원하지 않습니다.

  • Play 스토어는 현재 aab 업로드 후 설치 시 기준 프로필을 사용하는 데까지 10~14시간이 소요됩니다. 이 기간에 앱을 다운로드하는 사용자는 백그라운드 dexopt가 실행될 때까지(익일이 될 수 있음) 혜택이 표시되지 않습니다. 현재 이를 적극적으로 개선하는 중입니다.

  • Play 스토어 이외의 앱 배포 채널은 설치 시 기준 프로필 사용을 지원하지 않을 수도 있습니다. 이러한 채널을 통한 앱 사용자는 백그라운드 dexopt가 실행될 때까지 이점을 누릴 수 없습니다(익일이 될 수 있음).