0Pricing
Android Academy · 강의

성능 측정

프로파일러, 추적과 벤치마크를 살펴봅니다.

성능 측정은(는) CoddyKit의 무료 Android Academy 강의입니다. 이것은 4개 중 1번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Android Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Android Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

최적화하기 전에 측정하기

성능 작업의 황금률은 먼저 측정하고 절대 추측하지 않는 것입니다. 무엇이 느린지에 대한 사람의 직감은 거의 항상 틀립니다.

이 레슨에서는 Android가 제공하는 성능 확인 도구를 배웁니다. 프로파일러, 시스템 추적, 마이크로벤치마크를 사용하면 성능을 확인할 수 있습니다. 측정할 수 있게 되면 모든 최적화가 막연한 추측이 아니라 데이터를 기반으로 한 결정이 됩니다.

  • 프로파일러는 CPU, 메모리, 에너지를 실시간으로 보여 줍니다.
  • 시스템 추적은 각 프레임의 시간이 정확히 어디에 쓰이는지 보여 줍니다.
  • 벤치마크는 빌드 간에 비교할 수 있는 반복 가능한 수치를 제공합니다.

16ms 프레임 예산

60Hz 화면에서는 시스템이 매 16.67ms마다 새 프레임을 그립니다. 앱이 그 시간 안에 프레임을 준비하지 못하면 프레임이 누락되고 사용자는 끊김(버벅거림)을 느낍니다. 120Hz 기기에서는 예산이 약 8ms로 줄어듭니다.

성능 개선의 핵심은 이 예산 안에 머무르는 것입니다. 아래 주석은 항상 머릿속으로 계산해야 하는 수식을 보여 줍니다.

// Frame budget math
// 60 Hz  -> 1000ms / 60  = 16.67ms per frame
// 90 Hz  -> 1000ms / 90  = 11.11ms per frame
// 120 Hz -> 1000ms / 120 =  8.33ms per frame
//
// Exceed the budget on the UI thread = a dropped frame = visible jank.
fun frameBudgetMs(refreshHz: Int): Double = 1000.0 / refreshHz

fun main() {
    println("60Hz  -> %.2f ms".format(frameBudgetMs(60)))
    println("120Hz -> %.2f ms".format(frameBudgetMs(120)))
}

Android Studio 프로파일러

Android Studio 프로파일러(보기 > 도구 창 > 프로파일러)는 실행 중인 앱에 연결하여 CPU, 메모리, 에너지 및 네트워크의 실시간 타임라인을 보여 줍니다.

  • CPU: 메서드 추적과 시스템 추적을 기록하여 부하가 큰 코드 경로를 찾습니다.
  • 메모리: 할당을 확인하고 힙 덤프를 캡처하여 누수를 찾습니다.
  • 에너지: 배터리를 소모시키는 웨이크락과 과도한 작업을 찾아냅니다.

항상 실제 기기에서 릴리스 방식 빌드를 프로파일링하세요. 디버그 빌드와 에뮬레이터에서는 최적화가 비활성화되어 수치가 실제와 다르게 나타납니다.

실제 시나리오를 위한 매크로벤치마크

Jetpack 매크로벤치마크 라이브러리는 실제 기기에서 전체 사용자 여정(시작, 스크롤, 탐색)을 측정하고 프레임 시간 및 시작 시간과 같은 안정적인 지표를 보고합니다.

앱을 실행하고 흐름을 수행하는 테스트를 작성하면 라이브러리가 이를 여러 번 실행하고 중앙값과 99번째 백분위수 수치를 제공합니다.

@RunWith(AndroidJUnit4::class)
class StartupBenchmark {
    @get:Rule val rule = MacrobenchmarkRule()

    @Test
    fun coldStartup() = rule.measureRepeated(
        packageName = "com.example.app",
        metrics = listOf(StartupTimingMetric()),
        iterations = 5,
        startupMode = StartupMode.COLD
    ) {
        pressHome()
        startActivityAndWait()
    }
}

부하가 큰 코드를 위한 마이크로벤치마크

단일 함수가 얼마나 빠른지 알아야 할 때는 마이크로벤치마크 라이브러리를 사용하세요. 이 라이브러리는 코드를 촘촘한 반복문에서 실행하고 JIT를 예열한 뒤, 잡음을 제거한 연산당 나노초를 보고합니다.

파서, 직렬화기, 정렬 또는 자주 호출하는 CPU 중심 도우미에 사용하세요.

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

    @Test
    fun parseLargePayload() {
        val raw = loadSampleJson()
        benchmarkRule.measureRepeated {
            val parsed = parseUsers(raw) // code under test
            // assertEquals avoids the compiler dropping the result
            assertTrue(parsed.isNotEmpty())
        }
    }
}

Perfetto를 사용한 시스템 추적

시스템 추적은 모든 스레드와 시스템이 프레임마다 수행한 작업을 캡처합니다. Android Studio의 CPU 프로파일러로 이를 기록할 수 있으며, adb로 캡처한 뒤 Perfetto(ui.perfetto.dev)에서 열 수도 있습니다.

추적에는 악명 높은 빨간 프레임, 기본 스레드에서 수행된 작업, GPU에서 소요된 시간이 표시됩니다. 끊김을 진단하는 데 가장 좋은 단일 도구입니다.

# Record a 5-second system trace from the command line
adb shell perfetto -o /data/misc/perfetto-traces/trace.perfetto-trace \
  -t 5s sched freq idle am wm gfx view

# Pull it to your machine, then open in https://ui.perfetto.dev
adb pull /data/misc/perfetto-traces/trace.perfetto-trace

사용자 지정 추적 구간

기본적으로 추적에는 프레임워크 작업이 표시됩니다. 직접 작성한 코드에 이름을 붙이려면 이름이 지정된 추적 구간으로 감싸세요. 그러면 이 이름이 Perfetto에서 라벨이 지정된 블록으로 표시되어 부하가 큰 지점을 쉽게 찾을 수 있습니다.

androidx.tracing API를 사용하면 디버그 추적과 벤치마크 추적 모두에 구간이 표시됩니다.

import androidx.tracing.trace

fun loadDashboard(repo: Repo): Dashboard {
    return trace("loadDashboard") {
        val user = trace("fetchUser") { repo.user() }
        val feed = trace("fetchFeed") { repo.feed() }
        Dashboard(user, feed)
    }
}
// In Perfetto you will now see named slices:
// loadDashboard > fetchUser, fetchFeed

런타임에 누락된 프레임 추적하기

항상 프로파일러가 연결되어 있는 것은 아닙니다. JankStats는 실행 중인 앱 내부에서 끊김이 발생한 프레임을 보고하는 Jetpack 라이브러리이므로, 이를 기록하거나 집계 데이터를 분석 시스템으로 보낼 수 있습니다.

이 라이브러리는 창에 연결되어 프레임이 예산을 초과할 때마다 콜백을 호출합니다.

val jankStats = JankStats.createAndTrack(window) { frameData ->
    if (frameData.isJank) {
        Log.w("Jank", "Janky frame: ${frameData.frameDurationUiNanos} ns")
        analytics.logJank(frameData.frameDurationUiNanos)
    }
}

// Pause/resume with your screen lifecycle
override fun onResume() { super.onResume(); jankStats.isTrackingEnabled = true }
override fun onPause() { super.onPause(); jankStats.isTrackingEnabled = false }

수치 읽기: 백분위수

단일 평균값만으로는 문제를 숨길 수 있습니다. 평균 프레임 시간이 10ms이지만 99번째 백분위수가 40ms라면 프레임의 1%에서 끊김이 발생하며 사용자가 이를 느낍니다. 평균만 보지 말고 항상 P50, P90, P99를 확인하세요.

벤치마크가 이 수치를 대신 보고합니다. 아래 도우미는 원시 프레임 샘플로 그 개념을 보여 줍니다.

fun percentile(samplesMs: List<Double>, p: Int): Double {
    val sorted = samplesMs.sorted()
    val index = ((p / 100.0) * (sorted.size - 1)).toInt()
    return sorted[index]
}

fun main() {
    val frames = listOf(8.0, 9.0, 10.0, 9.5, 11.0, 40.0, 9.0, 10.5)
    println("P50 = %.1f ms".format(percentile(frames, 50)))
    println("P99 = %.1f ms".format(percentile(frames, 99)))
}

반복 가능한 측정 작업 흐름

신뢰할 수 있는 수치는 체계적인 과정에서 나옵니다. 매번 다음 확인 목록을 따르세요.

  • 릴리스 / 디버깅 불가 빌드(R8 활성화)를 사용합니다.
  • 전원에 연결하고 열 상태가 안정적인 실제 기기에서 실행합니다.
  • 시나리오를 여러 번 반복하고 중앙값을 보고합니다.
  • 한 번에 한 가지만 변경하고 다시 측정합니다.
  • 저장된 기준선과 비교하여 개선을 입증할 수 있게 합니다.

이 과정을 생략하면 실제 문제를 해결하는 대신 잡음을 쫓게 됩니다.

시간은 실제로 어디에 쓰이는가

대부분의 Android 끊김은 몇 가지 원인에서 발생합니다. 원인을 알면 도구를 어디에 사용해야 할지 알 수 있습니다.

  • 기본 스레드 작업: UI 스레드에서 수행하는 디스크/네트워크/JSON 작업
  • 과도한 재구성: Compose가 지나치게 많은 부분을 다시 그리는 경우
  • 할당 churn: 가비지 컬렉션으로 인한 일시 중지
  • 레이아웃 단계: 지나치게 깊게 중첩되었거나 반복해서 측정되는 레이아웃

이 과정의 다음 수업에서는 재구성, 메모리 누수 및 시작 문제를 직접 다룹니다. 무엇을 먼저 수정할지 알려 주는 것은 측정입니다.

빠른 확인

실제 기기에서 여러 번 실행하면서 앱의 콜드 시작에 걸리는 시간을 안정적이고 반복 가능하게 측정하려고 합니다. 어떤 도구가 가장 적합할까요?

복습: 이제 측정할 수 있습니다

무엇이든 변경하기 전에 성능을 눈에 보이게 만드는 방법을 배웠습니다.

  • 16ms 프레임 예산이 매끄러운 화면과 끊기는 화면을 구분합니다.
  • Android Studio 프로파일러가 CPU, 메모리 및 에너지를 실시간으로 보여 줍니다.
  • 매크로벤치마크와 마이크로벤치마크가 반복 가능한 수치를 제공합니다.
  • Perfetto 추적과 사용자 지정 trace() 구간으로 시간이 어디에 쓰이는지 확인할 수 있습니다.
  • JankStats가 실행 중인 앱 내부에서 끊김을 보고합니다.
  • P50/P90/P99를 확인하고, 실제 기기에서 릴리스 빌드를 프로파일링하며, 한 번에 한 가지씩 변경하세요.

다음으로 Compose 끊김의 가장 큰 원인 중 하나인 재구성에 이를 적용해 보겠습니다.

자주 묻는 질문

“성능 측정” 강의는 무료인가요?

네 — “성능 측정” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Android Academy 강의 전체를 잠금 해제할 수 있습니다. Android Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“성능 측정”에서 뭘 배우나요?

프로파일러, 추적과 벤치마크를 살펴봅니다. 브라우저에서 직접 실행하는 실습 코드로 Android Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Android Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Android Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 1번째 강의입니다.

“성능 측정” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Android Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Android Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 성능 측정
  2. 재구성 길들이기
  3. 메모리 누수와 해결 방법
  4. 시작 시간과 기준선 프로필
← Android Academy(으)로 돌아가기