Jetpack Benchmark 라이브러리 소개

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

사용 사례로는 RecyclerView 스크롤, 데이터베이스 쿼리 실행, 코드에서 더 빠르게 처리하고자 하는 느린 부분 측정 등이 있습니다.

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

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

빠른 시작

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

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

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

    project_root/module_dir/build.gradle

    Groovy

    dependencies {
        androidTestImplementation 'androidx.benchmark:benchmark-junit4:1.1.0-alpha13'
    }
    

    Kotlin

    dependencies {
        androidTestImplementation("androidx.benchmark:benchmark-junit4:1.1.0-alpha13")
    }
    
  2. 테스트 매니페스트에서 디버깅을 중지하려면 다음과 같이 <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 benchmarkSomeWork() {
            final BenchmarkState state = benchmarkRule.getState();
            while (state.keepRunning()) {
                doSomeWork();
            }
        }
    }
    

벤치마킹하는 경우

벤치마크를 작성하기 전에 코드를 프로파일링하는 것이 좋습니다. 그러면 최적화할 가치가 있는 높은 비용의 작업을 찾는 데 도움이 됩니다. 작업 실행 중에 발생하는 상황을 표시하여 작업이 느린 이유를 노출할 수도 있습니다. 우선순위가 낮은 스레드에서 실행, 디스크 액세스 권한으로 인한 절전 모드, 비용이 많이 드는 함수의 예기치 않은 호출(예: 비트맵 디코딩)을 예로 들 수 있습니다.

TraceCompat API(또는 -ktx 래퍼)를 통해 맞춤 트레이스 포인트를 추가하면 Android 스튜디오 CPU 프로파일러 또는 Systrace에서 확인할 수 있습니다.

Kotlin

fun proccessBitmap(bitmap: Bitmap): Bitmap {
    trace("processBitmap") {
        // perform work on bitmap...
    }
}

자바

public Bitmap processBitmap(Bitmap bitmaptest) {
    TraceCompat.beginSection("processBitmap");
    try {
        // perform work on bitmap...
    } finally {
        TraceCompat.endSection();
    }
}

벤치마킹 대상

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

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

캐싱

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

자주 실행되지 않는 코드

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

이러한 종류의 코드를 벤치마킹하려면 앱 실행과 스크롤 성능 같은 보다 높은 수준의 사용자 상호작용 측정을 지원하는 Jetpack Macrobenchmark를 사용하는 것이 좋습니다.

전체 프로젝트 설정

일회성이 아닌 정기적인 벤치마킹을 실행하도록 벤치마킹을 설정하려면 벤치마크를 자체 모듈로 격리합니다. 이렇게 하면 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<Context>()
        val inflater = LayoutInflater.from(context)
        val root = FrameLayout(context)

        benchmarkRule.measureRepeated {
            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<Context>();
        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 ... >
  <!-- This attribute is "false" by default on apps targeting Android 10. -->
  <application android:requestLegacyExternalStorage="true" ... >
    ...
  </application>
</manifest>

자세한 내용은 범위 지정 저장소를 일시적으로 선택 해제를 참고하세요.

클록 안정성

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

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

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

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

Groovy

buildscript {
    ...
    dependencies {
        ...
        classpath "androidx.benchmark:benchmark-gradle-plugin:1.1.0-alpha13"
    }
}

Kotlin

buildscript {
    ...
    dependencies {
        ...
        classpath("androidx.benchmark:benchmark-gradle-plugin:1.1.0-alpha13")
    }
}

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

Groovy

plugins {
    id 'com.android.app'
    id 'androidx.benchmark'
}
...

Kotlin

plugins {
    id("com.android.app")
    id("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에서 다음과 같이 설정할 수 있습니다.

Groovy

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

Kotlin

android {
    defaultConfig {
        testInstrumentationRunnerArguments(mapOf(
            "androidx.benchmark.suppressErrors" to "DEBUGGABLE"
        ))
    }
}

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

프로파일링

벤치마크를 프로파일링하여 측정된 코드가 느리게 실행되는 이유를 조사할 수 있습니다.

벤치마크 모듈의 build.gradle 파일에 다음을 추가하세요.

Groovy

android {
    defaultConfig {
        // must be one of: 'None', 'StackSampling', or 'MethodTracing'
        testInstrumentationRunnerArgument 'androidx.benchmark.profiling.mode', 'StackSampling'
    }
}

Kotlin

android {
    defaultConfig {
        // must be one of: 'None', 'StackSampling', or 'MethodTracing'
        testInstrumentationRunnerArgument = mapOf(
            "androidx.benchmark.profiling.mode", "StackSampling"
        )
    }
}

벤치마크를 실행하면 출력 .trace 파일이 JSON 결과와 함께 호스트의 디렉터리에 복사됩니다. Android 스튜디오에서 File > Open을 사용하여 이 파일을 열고 CPU 프로파일러에서 프로파일링 결과를 검사합니다.

메서드 추적

메서드 추적을 사용하면 메서드 트레이스를 캡처하기 전에 준비 작업을 통해 벤치마크가 호출한 모든 메서드를 기록합니다. 성능 결과는 각 메서드 시작/종료 캡처의 오버헤드에 크게 영향을 받습니다.

스택 샘플링

스택 샘플링을 사용하면 준비가 완료된 후 벤치마크가 호출 스택을 샘플링합니다. 계측 인수를 사용하여 샘플링 주파수샘플링 지속 시간을 제어할 수 있습니다.

Android 10(API 29) 이상에서 스택 샘플링은 Simpleperf를 사용하여 C++ 코드를 비롯한 앱 호출 스택을 샘플링합니다. Android 9(API 28) 이하에서는 Debug.startMethodTracingSampling을 사용하여 스택 샘플을 캡처합니다.

메서드 추적 및 샘플링을 사용하는 방법을 자세히 알아보려면 CPU 프로파일링 구성을 참고하세요.

벤치마킹 샘플

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

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

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

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

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

추가 리소스

탐색에 관해 자세히 알아보려면 다음 추가 리소스를 참고하세요.

블로그

의견 보내기

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