Android Baseline Profile & Macrobenchmark 사용해보기

wonsohana·2024년 10월 9일

구글 공식문서 를 토대로 시키는 대로 했으나… 나의 경우 정상적으로 동작하지 않았다.

하여 내가 했던 방식을 정리해 보려고 한다.

1. Android Baseline 모듈 추가

먼저 Baseline Profile 을 사용할 수 있도록 모듈을 추가해주자.

구글 문서에 따라 모듈 중에서 Baseline Profile Generator 라는 모듈을 선택하여 설치해주도록 한다.

만약 에뮬레이터 에서 테스트를 원한다면 마지막에 Use Gradle Managed Device 를 선택해주면 된다.

이는 자동으로 정의되는 값이기 때문에, 별도로 만들고 싶다면 모듈 생성 후에 build.gradle 에서 수정하거나 추가할 수 있다.

나는 app 으로 테스트 할 예정이라 Target application 을 app 기준으로 했다. 만약 샘플앱이나 본인 프로젝트의 여러 모듈 별로 테스트를 하고 싶다면 아래 문서를 참고하면 된다.

https://velog.io/@hana/Android-Baseline-Profiles-정리2-Create-Baseline-Profiles

2. Android Baseline Profile 이 정상적으로 동작하는지 확인

일단 위의 모듈 생성 팝업에서 finish 를 클릭하고 나면 본인이 설정한 모듈명을 토대로 (나의 경우 baselinemodule 으로) 모듈이 생성된 것을 확인할 수 있다.

여기서 BaselineProfileGenerator 클래스를 선택해보면 무언가 코드가 자동으로 짜여 있을 것이다.

코드의 내용을 잠깐 살펴보고 가자.


@RunWith(AndroidJUnit4::class)
@LargeTest
class BaselineProfileGenerator {

    @get:Rule
    val rule = BaselineProfileRule()

    @Test
    fun generate() {
		    // rule.collect() 의 매개변수에 packageName 는 필수값이다. 
		    // 본인의 packageName 를 직접 넣을 수도 있다.
        rule.collect(
            packageName = InstrumentationRegistry.getArguments().getString("targetAppId")
                ?: throw Exception("targetAppId not passed as instrumentation runner arg"),

            // 앱 시작과 관련된 테스트는 반드시 includeInStartProfile 이 true 로 설정된 규칙 블록 안에 있어야 하며,
            // 반대로 앱 시작과 관련이 없는 테스트는 Startup Profile 에 포함되지 않도록 해야 최적의 성능을 유지할 수 있다
            includeInStartupProfile = true
        ) {
           
           // 기본적으로 앱을 실행하고, 앱의 첫번째 프레임이 렌더링 될 때까지 기다린디.
            pressHome()
            startActivityAndWait()

            // 아래의 주석은 테스트해볼 수 있는 여러 예제들이다. 
            // nowInAndroid 앱에서 아래의 예제를 테스트 해볼 수 있다.
            // https://github.com/android/nowinandroid
            // For example:
            // 1. Wait until the content is asynchronously loaded
            // 2. Scroll the feed content
            // 3. Navigate to detail screen
        }
    }
}

BaselineProfileGenerator 좌측의 run 를 실행해보자.

나의 경우 variants 를 all 로 선택하여 실행했는데, current 를 통해 현재 variant 로만 실행하고 싶다면 먼저 현재의 variant 를 release 로 지정하면 정상적으로 실행된다.

정상적으로 실행이 완료되었다면 output 에서 해당 정보를 확인할 수 있다.

대략 이렇게 build/outputs 아래에 basline 과 관련된 파일이 생겼다면, (아마) 실행이 되었다고 볼 수 있다.

파일의 네이밍은 본인의 단말, flavor 등에 의해 달라질 수 있다.

💡 배포
Baseline Profile 모듈을 추가했다면 별도로 배포 시 작업할 것은 없다. 앞으로는 자동으로 build.gradle 에서 baseline profile 이 추가되어 배포된다.

3. 성능 개선 확인하기

모듈 생성 시 만들어진 클래스 중에 StartUpBenchmakrs 를 통해 성능이 얼마나 개선되었는지 확인할 수 있다.

여기서 Macrobenchmark 가 사용되었다는 것을 알 수 있다.

💡 Macrobenchmark 란?
- StartUp, UI 인터렉션, 애니메이션 등 사용자의 end-to-end 요소에 대한 성능을 측정해주는 도구
- 코드를 직접 호출하지 않고, UiAutomator 를 통해 터치, 클릭, 스와이프 등과 같은 인터렉션을 주며 성능을 측정할 수 있다
- 앱 시작, 프레임 시간, 추적된 코드 섹션을 직접 측정하도록 애플리케이션의 컴파일, 시작, 중지를 제어할 수 있다.
- 구글 플레이 스토어를 통해 설치된 최적화 앱의 환경을 로컬에서 재현할 수 있다. - 반복 테스트를 통해 여러 테스트 실행 결과를 볼 수 있다.

benchmark 가 동작하기 위해서는 app 모듈의 variant 를 benchmark 로 변경해 주어야 한다. 이 때 다른 모듈들은 자동으로 release 로 변경된다.

모듈을 통해 기본적으로 추가된 코드를 살펴보자.

@RunWith(AndroidJUnit4::class)
@LargeTest
class StartupBenchmarks {

    @get:Rule
    val rule = MacrobenchmarkRule()

    @Test
    fun startupCompilationNone() =
        benchmark(CompilationMode.None())

    @Test
    fun startupCompilationBaselineProfiles() =
        benchmark(CompilationMode.Partial(BaselineProfileMode.Require))

    private fun benchmark(compilationMode: CompilationMode) {
        // The application id for the running build variant is read from the instrumentation arguments.
        rule.measureRepeated(
            packageName = InstrumentationRegistry.getArguments().getString("targetAppId")
                ?: throw Exception("targetAppId not passed as instrumentation runner arg"),
            metrics = listOf(StartupTimingMetric()),
            compilationMode = compilationMode,
            startupMode = StartupMode.COLD,
            iterations = 10,
            setupBlock = {
                pressHome()
            },
            measureBlock = {
                startActivityAndWait()
            }
        )
    }
}
  • packgeName : 성능을 측정할 어플리케이션을 지정한다. 측정 대상을 선택해야 하기 때문에 무조건 지정이 필요하다.
  • metrics : 측정하려는 정보 유형.
  • interations : 벤치마크 테스트가 반복될 횟수. 많이 반복할 수록 안정성, 신뢰도가 높아지는 대신 실행 시간이 길어진다.
  • startupMode : 벤치마크 시작 시 원하는 애플리케이션 시작 방식. Activity 시작 방식과 테스트 시작 시의 프로세스 상태를 변경할 수 있다.
    • StartupMode.COLD : Startup Benchmark 에 사용한다. startupBlock 과 measureBlock 사이에 앱 프로세스를 종료한다.
    • StartupMode.WARM : Frame timing Benchmark 에 사용한다. startupBlock 후에 프로세스가 종료되지 않는다. 프로세스를 종료하지 않고 모든 실행 중인 Activity 를 닫는다.
    • StartupMode.HOT : 각 Activity 별 캐싱 매커니즘을 측정하는데 사용되며 프로세스나 이전에 실행 중이던 Activity 도 재시작 되지 않는다.
    • Null : Macrobenchmark 가 아무곳도 하지 않는 모드. kiiProcess() 로 직접 컨트롤 한다.
  • setUpBlock : 원하는 측정 화면 전에 할 동작 지정
  • measureBlock : 벤치마크 중에 측정을 원하는 동작

이외에도 ComplilationMode 를 퇘 컴파일 모드를 지정할 수 있다.

  • DEFAULT: Baseline Profile 을 사용하여 앱을 부분적으로 사전 컴파일 할 수 있다. CompilationMode 매개변수를 별도로 지정하지 않은 경우 사용된다.
  • None() : 앱 컴파일 상태를 재설정하고 앱을 사전 컴파일 하지 않는다. JIT(Just In Tim) 컴파일은 앱 실행 중에 계속 사용 설정 된다.
  • Partial() : Baseline Profile 이나 준비 실행 또는 둘 다를 통해 앱을 사전 컴파일한다.
  • Full() : 전체 애플리케이션 코드를 사전 컴파일 한다. 앱 실행 중 runtime 성능 비교 및 JIT 최적화로 인한 성능 변동 요인을 제거할 수 있다.

애플리케이션 성능을 최적화하려면 DEFAULT 모드를 선택하면 되는데, 이 옵션이 Google Play 에서 앱을 설치했을 때의 환경과 유사하다고 한다.

Baseline Profile 을 통해 제공되는 성능의 이점을 비교하고 싶다면 컴파일 모드 None 과 Parital 으로 비교하면 된다.

emulator 를 사용하고자 하는 경우
baseline profile 모듈의 build.gradle 에 아래와 같이 코드를 추가해주어야 한다.
android {
	buildConfig {
		testInstrumentationRunnerArguments["androidx.benchmark.suppressErrors"] = "EMULATOR"	
	}
}

테스트를 실행하면 아래와 같이 결과를 확인할 수 있다. 모듈에서 자동으로 작성해준 코드는 앱이 처음 실행되었을 때 걸리는 시간을 측정한다.

결과를 보면 알 수 있듯이, BaselineProfile 을 사용한 것은 213.9ms 사용하지 않은 것은 259.4ms 로 차이가 있는 것을 확인할 수 있다.

profile
Android Developer

0개의 댓글