앱 코드 벤치마킹

Jetpack Benchmark 라이브러리를 사용하여 Android 스튜디오 내에서 Kotlin 기반 또는 자바 기반 코드를 빠르게 벤치마킹할 수 있습니다. 이 라이브러리는 준비 작업을 처리하고 코드 성능을 측정하며 벤치마킹 결과를 Android 스튜디오 콘솔에 출력합니다.

사용 사례로는 RecyclerView 스크롤, 특수한 View 계층 구조 확장, 데이터베이스 쿼리 실행 등이 있습니다.

지속적 통합에서 벤치마크 실행의 설명대로 지속적 통합(CI) 환경에서 라이브러리를 사용할 수 있습니다.

벤치마킹하려는 프로젝트에서 AndroidX를 아직 채택하지 않은 경우 Android 스튜디오를 사용해 기존 프로젝트 이전을 참조하세요.

빠른 시작

이 섹션에서는 코드를 모듈로 이동할 필요 없이 벤치마킹을 시도하는 빠른 단계를 제공합니다. 이 단계에는 정확한 성능 결과를 위해 디버깅을 중지하는 작업이 포함되므로 변경사항을 소스 제어 시스템에 커밋하지 않지만, 일회성으로 측정을 하려는 경우에는 유용할 수 있습니다.

일회성 벤치마킹을 빠르게 실행하려면 다음 단계를 따르세요.

  1. 라이브러리를 모듈의 build.gradle 파일에 추가합니다.

    project_root/module_dir/build.gradle

        dependencies {
            androidTestImplementation "androidx.benchmark:benchmark-junit4:1.0.0-beta01"
        }
        
  2. 테스트 manifest에서 디버깅을 중지하려면 다음과 같이 <application> 요소를 업데이트하여 디버깅을 일시적으로 강제 중지합니다.

    project_root/module_dir/src/androidTest/AndroidManifest.xml

    <!-- Important: disable debuggable for accurate performance results -->
        <application
            android:debuggable="false"
            tools:ignore="HardcodedDebugMode"
            tools:replace="android:debuggable"/>
        
  3. 벤치마크를 추가하려면 androidTest 디렉터리의 테스트 파일에 BenchmarkRule 인스턴스를 추가합니다. 벤치마크 작성에 관한 자세한 내용은 벤치마크 작성을 참조하세요.

    다음 코드 스니펫은 JUnit 테스트에 벤치마크를 추가하는 방법을 보여줍니다.

    Kotlin

        @RunWith(AndroidJUnit4::class)
        class MyBenchmark {
            @get:Rule
            val benchmarkRule = BenchmarkRule()
    
            @Test
            fun benchmarkSomeWork() = benchmarkRule.measureRepeated {
                doSomeWork()
            }
        }
        

    자바

        @RunWith(AndroidJUnit4.class)
        class MyBenchmark {
            @Rule
            public BenchmarkRule benchmarkRule = new BenchmarkRule();
    
            @Test
            public void myBenchmark() {
                final BenchmarkState state = benchmarkRule.getState();
                while (state.keepRunning()) {
                    doSomeWork();
                }
            }
        }
        

벤치마킹 대상

벤치마크는 앱에서 여러 번 실행되는 CPU 작업에 가장 유용합니다. 좋은 예로는 RecyclerView 스크롤, 데이터 변환/처리 및 반복적으로 사용되는 코드 조각이 있습니다.

다른 유형의 코드는 벤치마킹으로 측정하기 더 어렵습니다. 벤치마크는 루프에서 실행되기 때문에 자주 실행되지 않거나 여러 번 호출될 때 다르게 수행되는 코드는 벤치마킹에 적합하지 않을 수 있습니다.

캐싱

캐시만 측정하는 것을 피하세요. 예를 들어, 맞춤 뷰의 레이아웃 벤치마크가 레이아웃 캐시의 성능만 측정할 수 있는데, 이러한 상황을 피하려면 각 루프에 다른 레이아웃 매개변수를 전달하면 됩니다. 그러나 파일 시스템 성능을 측정할 때와 같은 다른 경우에는 OS가 루프에 있는 동안 파일 시스템을 캐시하기 때문에 이것이 어려울 수 있습니다.

자주 실행되지 않는 코드

애플리케이션 시작 중에 한 번만 실행되는 코드는 Android 런타임(ART)에서 JIT로 컴파일되지 않을 가능성이 큽니다. 따라서 이 코드가 루프에서 실행될 때 벤치마킹하는 것은 성능을 측정하는 현실적인 방법이 아닙니다.

이러한 종류의 코드는 앱에서 코드를 추적하거나 프로파일링하는 것이 좋습니다. 단, 이것이 시작 경로에 있는 코드를 벤치마킹할 수 없다는 의미는 아니며, 루프에서 실행되고 JIT로 컴파일될 가능성이 큰 코드를 선택해야 한다는 의미로 이해하시기 바랍니다.

전체 프로젝트 설정

일회성이 아니라 정기적인 벤치마킹을 실행하도록 벤치마킹을 설정하려면 벤치마크를 자체 모듈로 격리합니다. 이렇게 하면 debuggablefalse로 설정하는 것과 같은 구성이 다른 일반 테스트와 분리됩니다.

이를 위해서는 다음 작업을 완료해야 합니다.

  • 벤치마킹하려는 코드와 리소스가 아직 라이브러리 모듈에 없는 경우 이를 라이브러리 모듈에 배치합니다.

  • 벤치마크 자체를 보존할 새 라이브러리 모듈을 추가합니다.

샘플에서는 이러한 방식으로 프로젝트를 설정하는 방법의 예를 제공합니다.

Android 스튜디오 속성 설정

Android 스튜디오 3.5를 사용하는 경우 벤치마크 모듈 마법사 지원을 사용하도록 Android 스튜디오 속성을 수동으로 설정해야 합니다. Android 스튜디오 3.6 이상에서는 이런 설정이 필요 없습니다.

Android 스튜디오 템플릿을 벤치마킹에 사용할 수 있도록 하려면 다음 단계를 따르세요.

  1. Android 스튜디오 3.5에서 Help > Edit Custom Properties를 클릭합니다.

  2. 열린 파일에 다음 행을 추가합니다.

    npw.benchmark.template.module=true

  3. 파일을 저장하고 닫습니다.

  4. Android 스튜디오를 다시 시작합니다.

새 모듈 생성

벤치마킹 모듈 템플릿이 벤치마킹 설정을 자동으로 구성합니다.

모듈 템플릿을 사용하여 새 모듈을 생성하려면 다음 단계를 따르세요.

  1. 프로젝트 또는 모듈을 마우스 오른쪽 버튼으로 클릭하고 New > Module을 선택합니다.

  2. Benchmark Module을 선택하고 Next를 클릭합니다.

    그림 1. 벤치마크 모듈

  3. 모듈 이름을 입력하고 언어를 선택한 다음 Finish를 클릭합니다.

    벤치마크 디렉터리가 추가되고 debuggablefalse로 설정된 상태로 벤치마킹용으로 사전 구성된 모듈이 생성됩니다.

벤치마크 작성

벤치마크는 표준 계측 테스트입니다. 벤치마크를 생성하려면 라이브러리에서 제공되는 BenchmarkRule 클래스를 사용하세요. 활동을 벤치마킹하려면 ActivityTestRule 또는 ActivityScenarioRule을 사용합니다. UI 코드를 벤치마킹하려면 @UiThreadTest를 사용하세요.

다음 코드는 샘플 벤치마크를 보여줍니다.

Kotlin

    @RunWith(AndroidJUnit4::class)
    class ViewBenchmark {
        @get:Rule
        val benchmarkRule = BenchmarkRule()

        @Test
        fun simpleViewInflate() {
            val context = ApplicationProvider.getApplicationContext()
            val inflater = LayoutInflater.from(context)
            val root = FrameLayout(context)

            benchmarkRule.keepRunning {
                inflater.inflate(R.layout.test_simple_view, root, false)
            }
        }
    }
    

자바

    @RunWith(AndroidJUnit4::class)
    public class ViewBenchmark {
        @Rule
        public BenchmarkRule benchmarkRule = new BenchmarkRule();

        @Test
        public void simpleViewInflate() {
            Context context = ApplicationProvider.getApplicationContext();
            final BenchmarkState state = benchmarkRule.getState();
            LayoutInflater inflater = LayoutInflater.from(context);
            FrameLayout root = new FrameLayout(context);

            while (state.keepRunning()) {
                inflater.inflate(R.layout.test_simple_view, root, false);
            }
        }
    }
    

다음 코드 샘플과 같이 측정하지 않을 코드 섹션의 타이밍을 중지할 수 있습니다.

Kotlin

    @Test
    fun bitmapProcessing() = benchmarkRule.measureRepeated {
        val input: Bitmap = runWithTimingDisabled { constructTestBitmap() }
        processBitmap(input)
    }
    

자바

    @Test
    public void bitmapProcessing() {
        final BenchmarkState state = benchmarkRule.getState();
        while (state.keepRunning()) {
            state.pauseTiming();
            Bitmap input = constructTestBitmap();
            state.resumeTiming();

            processBitmap(input);
        }
    }
    

벤치마크 실행에 관한 자세한 내용은 벤치마크 실행을 참조하세요.

벤치마크 실행

Android 스튜디오에서 @Test를 실행하는 것처럼 벤치마크를 실행하세요. Android 스튜디오 3.4 이상에서는 콘솔에 전송된 출력을 볼 수 있습니다.

벤치마크를 실행하려면 모듈에서 benchmark/src/androidTest로 이동하고 Control+Shift+F10(Mac의 경우 Command+Shift+R)을 누릅니다. 그림 2에서와 같이 벤치마크 결과가 콘솔에 표시됩니다.

Android 스튜디오의 벤치마킹 출력

그림 2. Android 스튜디오의 벤치마킹 출력

정기적으로 명령줄에서 connectedCheck를 실행합니다.

./gradlew benchmark:connectedCheck
    

데이터 수집

추가 측정항목 및 기기 정보가 포함된 전체 벤치마크 보고서는 JSON 형식으로 되어 있습니다.

기본적으로 androidx.benchmark Gradle 플러그인이 JSON 출력을 사용 설정합니다. Gradle 기반이 아닌 빌드 환경에서 수동으로 JSON 출력을 사용 설정하려면 true로 설정된 계측 인수 androidx.benchmark.output.enable을 전달합니다.

다음은 adb shell am instrument 명령어 사용의 예입니다.

    adb shell am instrument -w -e "androidx.benchmark.output.enable" "true" com.android.foo/androidx.benchmark.junit4.AndroidBenchmarkRunner
    

기본적으로 JSON 보고서는 테스트 APK의 외부 공유 다운로드 폴더에 있는 기기의 디스크에 작성되며 일반적으로 다음 위치에 있습니다.

    /storage/emulated/0/Download/app_id-benchmarkData.json
    

계측 인수 additionalTestOutputDir을 사용하여 기기에서 벤치마크 보고서가 저장되는 위치를 구성할 수 있습니다.

    adb shell am instrument -w -e "androidx.benchmark.output.enable" "true" -e "additionalTestOutputDir" "/path_to_a_directory_on_device_test_has_write_permissions_to/" com.android.foo/androidx.benchmark.junit4.AndroidBenchmarkRunner
    

JSON 보고서가 기기에서 호스트로 복사됩니다. 이러한 보고서는 다음의 호스트 컴퓨터에 작성됩니다.

    project_root/module/build/outputs/connected_android_test_additional_output/debugAndroidTest/connected/device_id/app_id-benchmarkData.json
    

Android Gradle 플러그인 3.6 이상

Gradle을 사용하여 명령줄에서 벤치마크를 실행하는 경우 Android Gradle 플러그인 3.6 이상을 사용하는 프로젝트는 프로젝트의 gradle.properties 파일에 다음 플래그를 추가할 수 있습니다.

    android.enableAdditionalTestOutput=true
    

이렇게 하면 실험용 Android Gradle 플러그인 기능이 API 16 이상을 실행하는 기기의 벤치마크 보고서를 가져올 수 있습니다.

Android Gradle 플러그인 3.5 이하

이전 버전의 Android Gradle 플러그인을 사용하는 사용자의 경우 androidx.benchmark Gradle 플러그인은 기기에서 호스트로 JSON 벤치마크 보고서 복사를 처리합니다.

AGP 3.5 이하를 사용하며 API 수준 29 이상을 타겟팅하는 경우 벤치마크의 androidTest 디렉터리에 있는 Android manifest에 플래그를 추가하여 레거시 외부 저장소 동작을 사용 설정해야 합니다. 범위 지정 저장소 선택 해제와 관련된 자세한 내용은 필터링된 보기 선택 해제를 참조하세요.

    <manifest ... >
      <!-- This attribute is "false" by default on apps targeting Android 10. -->
      <application android:requestLegacyExternalStorage="true" ... >
        ...
      </application>
    </manifest>
    

클록 안정성

휴대기기의 클록은 높은 상태(성능을 위해)에서 낮은 상태(전원을 절약하기 위해 또는 기기에 열이 발생하는 경우)까지 동적으로 변경됩니다. 이와 같이 변화하는 클록으로 인해 벤치마크 숫자가 크게 달라질 수 있으므로 라이브러리에서 이 문제를 처리하는 방법을 제공합니다.

클록 잠금(루팅이 필요함)

클록 잠금은 안정적인 성능을 얻는 가장 좋은 방법입니다. 클록을 잠그면 클록이 기기를 과열시킬 만큼 높아지거나 벤치마크가 CPU를 완전히 활용하지 않는 경우 낮아지지 않습니다. 클록을 잠그는 것은 안정적인 성능을 보장하는 가장 좋은 방법이지만 adb 루팅이 필요하기 때문에 대부분의 기기에서 지원되지 않습니다.

클록을 잠그려면 제공된 도우미 플러그인을 기본 build.gradle 파일의 최상위 프로젝트 클래스 경로에 추가하세요.

buildscript {
        ...
        dependencies {
            ...
            classpath "androidx.benchmark:benchmark-gradle-plugin:1.0.0-beta01"
        }
    }
    

벤치마킹할 모듈의 build.gradle에 플러그인을 적용하세요.

apply plugin: com.android.app
    apply plugin: androidx.benchmark
    ...
    

이렇게 하면 ./gradlew lockClocks./gradlew unlockClocks를 비롯한 벤치마킹 Gradle 작업이 프로젝트에 추가됩니다. 이러한 작업을 사용하여 adb를 통해 기기의 CPU를 잠그거나 잠금 해제하세요.

adb에 여러 기기가 표시되면 환경 변수 ANDROID_SERIAL을 사용하여 Gradle 작업이 작동할 기기를 지정하세요.

    ANDROID_SERIAL=device-id-from-adb-devices ./gradlew lockClocks
    

지속적인 성능 모드

Window.setSustainedPerformanceMode()는 앱에서 더 낮은 최대 CPU 주파수를 선택할 수 있도록 하는 기능이며 일부 기기에서 지원됩니다. 지원되는 기기에서 실행되는 경우 벤치마크 라이브러리는 이 API를 사용하는 동시에 자체 활동을 실행하여 열 제한을 방지하고 결과를 안정화합니다.

이 기능은 Gradle 플러그인에서 설정된 testInstrumentationRunner를 통해 기본적으로 사용 설정됩니다. 맞춤 실행기를 사용하려면 AndroidBenchmarkRunner를 서브클래스로 만들어 testInstrumentationRunner로 사용할 수 있습니다.

벤치마크가 다른 앱 드로잉 없이 포그라운드에서 실행될 수 있도록 실행기가 불투명한 전체화면 활동을 시작합니다.

자동 실행 일시중지

클록 잠금 및 지속적인 성능이 모두 사용되지 않으면 라이브러리에서 자동 열 제한 감지를 실행합니다. 사용 설정된 경우 내부 벤치마크가 주기적으로 실행되어 CPU 성능을 저하할 정도로 기기 온도가 높아진 시기를 확인합니다. CPU 성능 저하가 감지되면 라이브러리가 실행을 일시중지하여 기기의 열을 식히고 현재 벤치마크를 다시 시도합니다.

구성 오류

라이브러리는 다음 조건을 감지하여 프로젝트 및 환경이 출시 당시의 정확한 성능을 제공하도록 설정되었는지 확인합니다.

  • Debuggablefalse로 설정되어 있습니다.
  • 에뮬레이터가 아닌 실제 기기가 사용됩니다.
  • 기기가 루팅된 경우 시계가 잠깁니다.
  • 기기의 배터리 수준이 충분합니다.

위의 검사 중 하나라도 실패하면 벤치마크에서 오류가 발생하여 부정확한 측정을 방지합니다.

이러한 오류를 경고로 표시하지 않고 이로 인해 오류가 발생하거나 벤치마크가 중지되지 않도록 하려면 억제할 오류 유형을 쉼표로 구분된 목록으로 계측 인수 androidx.benchmark.suppressErrors에 전달하세요.

    adb shell am instrument -w -e "androidx.benchmark.suppressErrors" "DEBUGGABLE" com.android.foo/androidx.benchmark.junit4.AndroidBenchmarkRunner
    

Gradle에서 다음과 같이 설정할 수 있습니다.

    android {
        defaultConfig {
            testInstrumentationRunnerArgument 'androidx.benchmark.suppressErrors', 'DEBUGGABLE'
        }
    }
    

오류를 억제하면 벤치마크가 잘못 구성된 상태로 실행될 수 있지만 테스트 이름 앞에 오류를 추가하여 벤치마크의 출력이 의도적으로 손상됩니다. 즉, 디버그 가능한 벤치마크가 위와 같이 억제되면 테스트 이름 앞에는 DEBUGGABLE_이 추가됩니다.

벤치마킹 샘플

다음 프로젝트에서 샘플 벤치마크 코드를 사용할 수 있습니다.

샘플 프로젝트는 다음과 같습니다.

  • BenchmarkSample: 벤치마크 모듈을 사용하여 코드 및 UI를 측정하는 방법을 보여주는 독립 실행형 샘플입니다.

  • PagingWithNetworkSample: RecyclerView 성능을 벤치마킹하는 방법을 보여주는 Android 아키텍처 구성요소 샘플입니다.

  • WorkManagerSample: WorkManager 작업자를 벤치마킹하는 방법을 보여주는 Android 아키텍처 구성요소 샘플입니다.

의견 보내기

벤치마킹을 사용할 때 문제를 보고하거나 기능 요청을 제출하려면 공개 Issue Tracker를 참조하세요.