수동으로 기준 프로필 생성 및 측정

수동 작업을 줄이고 전체적인 확장성을 높일 수 있도록 Jetpack Macrobenchmark 라이브러리를 사용하여 프로필 규칙 생성을 자동화하는 것이 적극 권장됩니다. 단, 앱에서 프로필 규칙을 수동으로 생성 및 측정하는 것이 가능합니다.

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

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;

샘플 컴파일러 탐색기 프로젝트에서 프로필 규칙을 수정해 볼 수 있습니다. 컴파일러 탐색기는 사람이 읽을 수 있는 ART 프로필 형식 (HRF)만 지원하므로 와일드 카드는 지원되지 않습니다.

규칙 문법

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

[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가 이 프로필을 사용합니다.

수동으로 기준 프로필 수집

Macrobenchmark 라이브러리를 설정하지 않고 기준 프로필을 수동으로 생성하고 중요한 사용자 여정의 UI 자동화를 만들 수 있습니다. Macrobenchmark를 사용하면 좋지만 항상 사용할 수 있는 것은 아닙니다. 예를 들어 Gradle 이외의 빌드 시스템을 사용하는 경우 기준 프로필 Gradle 플러그인을 사용할 수 없습니다. 이 경우 기준 프로필 규칙을 수동으로 수집할 수 있습니다. 이 작업은 API 34 이상을 실행하는 기기나 에뮬레이터를 사용하는 경우 훨씬 더 쉽습니다. 더 낮은 API 수준에서도 여전히 가능하지만 루트 액세스 권한이 필요하며 AOSP 이미지를 실행하는 에뮬레이터를 사용해야 합니다. 다음을 실행하여 직접 규칙을 수집해도 됩니다.

  1. 테스트 기기에 앱의 출시 버전을 설치합니다. 정확한 프로필을 위해 앱 빌드 유형은 R8에 최적화되고 디버그할 수 없어야 합니다.
  2. 프로필이 아직 컴파일되지 않았는지 확인합니다.

    API 34 이상

    adb shell cmd package compile -f -m verify $PACKAGE_NAME
    adb shell pm art clear-app-profiles $PACKAGE_NAME

    API 33 이하

    adb root
    adb shell cmd package compile --reset $PACKAGE_NAME

    APK에 Jetpack 프로필 설치 프로그램 라이브러리의 종속 항목이 있는 경우 라이브러리는 APK를 처음 실행할 때 프로필을 부트스트랩합니다. 이는 프로필 생성 프로세스를 방해할 수 있으므로 다음 명령어로 사용 중지합니다.

    adb shell am broadcast -a androidx.profileinstaller.action.SKIP_FILE $PACKAGE_NAME/androidx.profileinstaller.ProfileInstallReceiver
  3. 앱을 실행하고 프로필을 수집할 중요한 사용자 여정을 수동으로 탐색합니다.
  4. ART에 프로필을 덤프하라는 프롬프트를 표시합니다. APK에 Jetpack 프로필 설치 프로그램 라이브러리의 종속 항목이 있는 경우 이를 사용하여 프로필을 덤프합니다.

    adb shell am broadcast -a androidx.profileinstaller.action.SAVE_FILE $PACKAGE_NAME/androidx.profileinstaller.ProfileInstallReceiver
    adb shell am force-stop $PACKAGE_NAME
    프로필 설치 프로그램을 사용하지 않는다면 다음 명령어를 사용하여 에뮬레이터에서 수동으로 프로필을 덤프합니다.

    adb root
    adb shell killall -s SIGUSR1 $PACKAGE_NAME
    adb shell am force-stop $PACKAGE_NAME
  5. 프로필 생성이 완료될 때까지 5초 이상 기다립니다.
  6. 생성된 바이너리 프로필을 텍스트로 변환합니다.

    API 34 이상

    adb shell pm dump-profiles --dump-classes-and-methods $PACKAGE_NAME

    API 33 이하

    참조 프로필 또는 현재 프로필이 만들어졌는지 확인합니다. 참조 프로필은 다음 위치에 있습니다.

    /data/misc/profiles/ref/$$PACKAGE_NAME/primary.prof

    현재 프로필은 다음 위치에 있습니다.

    /data/misc/profiles/cur/0/$PACKAGE_NAME/primary.prof

    APK의 위치를 확인합니다.

    adb root
    adb shell pm path $PACKAGE_NAME

    다음과 같이 변환을 실행합니다.

    adb root
    adb shell profman --dump-classes-and-methods --profile-file=$PROFILE_PATH --apk=$APK_PATH > /data/misc/profman/$PACKAGE_NAME-primary.prof.txt

  7. adb를 사용하여 덤프된 프로필을 기기에서 가져옵니다.

    adb pull /data/misc/profman/$PACKAGE_NAME-primary.prof.txt PATH_TO_APP_MODULE/src/main/

이렇게 하면 생성된 프로필 규칙을 가져와 앱 모듈에 설치할 수 있습니다. 다음에 앱을 빌드할 때는 기준 프로필이 포함됩니다. 설치 문제의 단계에 따라 이를 확인하세요.

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

벤치마킹을 통해 앱 개선사항을 측정하는 것이 적극 권장됩니다. 단, 개선사항을 수동으로 측정하고 싶다면 최적화되지 않은 앱 시작을 참고용으로 측정하는 것으로 시작할 수 있습니다.

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.
adb shell am start-activity -W -n $PACKAGE_NAME/.ExampleActivity \
 | grep "TotalTime"

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

# Unzip the Release APK first.
unzip release.apk
# Create a ZIP archive.
# The name should match the name of the APK.
# Copy `baseline.prof{m}` and rename it `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"

기준 프로필과 profgen

이 섹션에서는 기준 프로필의 간단한 바이너리 버전을 빌드할 때 profgen 도구가 어떤 작업을 하는지 설명합니다.

Profgen-cli는 프로필 컴파일, 검사, ART 프로필 트랜스파일을 지원하므로 타겟 SDK 버전과 관계없이 Android 지원 기기에 설치할 수 있습니다.

Profgen-cli는 기준 프로필의 HRF를 컴파일된 형식으로 컴파일하는 CLI입니다. 이 CLI는 Android SDK의 일부로 cmdline-tools 저장소에 포함되어 제공됩니다.

이러한 기능은 studio-main 브랜치에서 사용할 수 있습니다.

 ../cmdline-tools/latest/bin
apkanalyzer
avdmanager
lint
profgen
retrace
screenshot2
sdkmanager

Profgen-cli를 사용하여 간단한 바이너리 프로필 빌드

Profgen-cli에서 사용할 수 있는 명령어는 bin, validate, dumpProfile입니다. 사용 가능한 명령어를 보려면 profgen --help를 사용합니다.

  profgen --help
Usage: profgen options_list
Subcommands:
    bin - Generate Binary Profile
    validate - Validate Profile
    dumpProfile - Dump a binary profile to a HRF

Options:
    --help, -h -> Usage info

bin 명령어를 사용하여 간단한 바이너리 프로필을 생성합니다. 다음은 호출 예시입니다.

profgen bin ./baseline-prof.txt \
  --apk ./release.apk \
  --map ./obfuscation-map.txt \
  --profile-format v0_1_0_p \
  --output ./baseline.prof \

사용 가능한 옵션을 보려면 profgen bin options_list를 사용합니다.

Usage: profgen bin options_list
Arguments:
    profile -> File path to Human Readable profile { String }
Options:
    --apk, -a -> File path to apk (always required) { String }
    --output, -o -> File path to generated binary profile (always required)
    --map, -m -> File path to name obfuscation map { String }
    --output-meta, -om -> File path to generated metadata output { String }
    --profile-format, -pf [V0_1_0_P] -> The ART profile format version
      { Value should be one of [
         v0_1_5_s, v0_1_0_p, v0_0_9_omr1, v0_0_5_o, v0_0_1_n
        ]
      }
    --help, -h -> Usage info

첫 번째 인수는 baseline-prof.txt HRF의 경로를 나타냅니다.

Profgen-cli에는 APK의 출시 빌드 경로와 R8 또는 Proguard를 사용할 때 APK를 난독화하는 데 사용되는 난독화 맵도 필요합니다. 이를 통해 profgen은 컴파일된 프로필을 빌드할 때 HRF의 소스 기호를 상응하는 난독화된 이름으로 변환할 수 있습니다.

ART 프로필 형식은 이후 버전 및 이전 버전과 호환되지 않으므로, 필요한 경우 하나의 ART 프로필 형식을 다른 형식으로 트랜스코딩하는 데 사용할 수 있는 프로필 메타데이터(profm)를 profgen이 패키징하도록 프로필 형식을 제공해야 합니다.

프로필 형식 및 플랫폼 버전

프로필 형식을 선택할 때 다음 옵션을 사용할 수 있습니다.

프로필 형식 플랫폼 버전 API 수준
v0_1_5_s Android S+ 31+
v0_1_0_p Android P, Q, R 28-30
v0_0_9_omr1 Android O MR1 27
v0_0_5_o Android O 26
v0_0_1_n Android N 24-25

baseline.profbaseline.profm 출력 파일을 APK의 assets 또는 dexopt 폴더에 복사합니다.

난독화 맵

난독화 맵은 HRF가 소스 기호를 사용하는 경우에만 제공하면 됩니다. HRF가 이미 난독화된 출시 빌드에서 생성되어 매핑이 필요하지 않은 경우 이 옵션을 무시하고 출력을 assets 또는 dexopt 폴더에 복사하면 됩니다.

기존 방식의 기준 프로필 설치

기존 방식에서는 기준 프로필이 두 가지 방법 중 하나로 기기에 전송됩니다.

DexMetadata와 함께 install-multiple 사용

API 28 및 이후 버전을 실행하는 기기에서는 Play 클라이언트가 설치되는 APK 버전의 APK 및 DexMetadata(DM) 페이로드를 다운로드합니다. DM에는 프로필 정보가 포함되어 있으며, 이 정보가 기기의 패키지 관리자로 전달됩니다.

APK와 DM은 다음과 같은 명령어를 사용하여 단일 설치 세션의 일환으로 설치됩니다.

adb install-multiple base.apk base.dm

Jetpack ProfileInstaller

API 수준 29 및 이후 버전을 실행하는 기기에서는 Jetpack ProfileInstaller 라이브러리가 APK가 기기에 설치된 후에 assets 또는dexopt로 패키징된 프로필을 설치하는 대체 메커니즘을 제공합니다. ProfileInstallerProfileInstallReceiver에 의해 호출되거나 앱에 의해 직접 호출됩니다.

ProfileInstaller 라이브러리는 대상 기기의 SDK 버전에 따라 프로필을 트랜스코딩하여 기기의 cur 디렉터리(기기의 ART 프로필을 위한 패키지별 스테이징 디렉터리)에 복사합니다.

기기가 유휴 상태가 되면 기기의 bg-dexopt라는 프로세스에서 프로필을 선택합니다.

기준 프로필 사이드로드

이 섹션에서는 APK에 따라 기준 프로필을 설치하는 방법을 설명합니다.

androidx.profileinstaller를 사용하여 브로드캐스트

API 24 및 이후 버전을 실행하는 기기에서는 명령어를 브로드캐스트하여 프로필을 설치할 수 있습니다.

# Broadcast the install profile command - moves binary profile from assets
#     to a location where ART uses it for the next compile.
#     When successful, the following command prints "1":
adb shell am broadcast \
    -a androidx.profileinstaller.action.INSTALL_PROFILE \
    <pkg>/androidx.profileinstaller.ProfileInstallReceiver

# Kill the process
am force-stop <pkg>

# Compile the package based on profile
adb shell cmd package compile -f -m speed-profile <pkg>

기준 프로필을 갖는 대부분의 APK(Play 앱 45만 개 중 약 77,000개)에는 ProfileInstaller가 없지만 Compose를 사용하는 모든 APK에는 있습니다. 라이브러리가 ProfileInstaller의 종속 항목을 선언하지 않고 프로필을 제공할 수 있기 때문입니다. 프로필을 갖는 각 라이브러리에 종속 항목을 추가하는 것은 Jetpack부터 적용됩니다.

profgen 또는 DexMetaData와 함께 install-multiple 사용

API 28 및 이후 버전을 실행하는 기기에서는 앱에 ProfileInstaller 라이브러리가 없어도 기준 프로필을 사이드로드할 수 있습니다.

이렇게 하려면 Profgen-cli를 사용합니다.

profgen extractProfile \
        --apk app-release.apk \
        --output-dex-metadata app-release.dm \
        --profile-format V0_1_5_S # Select based on device and the preceding table.

# Install APK and the profile together
adb install-multiple appname-release.apk appname-release.dm

APK 분할을 지원하려면 APK당 한 번씩 위의 프로필 추출 단계를 실행합니다. 설치 시점에 각 APK 및 관련 .dm 파일을 전달합니다. 이때 APK 이름과 .dm 이름이 일치해야 합니다.

adb install-multiple appname-base.apk appname-base.dm \
appname-split1.apk appname-split1.dm

확인

프로필이 올바르게 설치되었는지 확인하려면 수동으로 앱 개선사항 측정하기 단계를 따르세요.

바이너리 프로필의 콘텐츠 덤프

간단한 바이너리 버전 기준 프로필의 콘텐츠를 검사하려면 Profgen-cli dumpProfile 옵션을 사용합니다.

Usage: profgen dumpProfile options_list
Options:
    --profile, -p -> File path to the binary profile (always required)
    --apk, -a -> File path to apk (always required) { String }
    --map, -m -> File path to name obfuscation map { String }
    --strict, -s [true] -> Strict mode
    --output, -o -> File path for the HRF (always required) { String }
    --help, -h -> Usage info

간단한 바이너리 버전은 DEX 오프셋만 저장하므로 dumpProfile이 클래스 이름과 메서드 이름을 재구성하려면 APK가 필요합니다.

기본적으로 엄격 모드가 사용 설정됩니다. 엄격 모드에서는 프로필과 APK의 DEX 파일 간의 호환성 검사를 실행합니다. 다른 도구에서 생성된 프로필을 디버그하려고 시도하면 호환성 오류가 발생하여 검사를 위해 덤프를 할 수 없게 될 수 있습니다. 이 경우 --strict false를 사용하여 엄격 모드를 중지할 수 있습니다. 그러나 대부분의 경우에는 엄격한 모드를 사용 설정해야 합니다.

난독화 맵은 선택사항입니다. 난독화 맵이 제공된 경우 사용 편의를 위해 난독화된 기호를 인간이 읽을 수 있는 버전으로 재매핑하는 데 도움이 됩니다.