lightbulb_outline Help shape the future of the Google Play Console, Android Studio, and Firebase. Start survey

CPU Profiler를 이용한 CPU activity 및 메서드 추적 검사

CPU Profiler는 앱의 CPU 사용량과 스레드 activity를 실시간으로 검사하고 메서드 추적을 기록하는 데 도움이 되므로, 앱의 코드를 최적화하고 디버그할 수 있습니다.

CPU Profiler를 열려면 다음 단계를 따르세요.

  1. View > Tool Windows > Android Profiler를 클릭합니다(툴바에서 Android Profiler 를 클릭해도 됨).
  2. Android Profiler 툴바에서 프로파일링하려는 기기 및 앱 프로세스를 선택합니다. USB를 통해 기기를 연결했는데 목록에 표시되지 않을 경우 USB 디버깅을 활성화했는지 확인하세요.
  3. CPU 타임라인에서 아무 곳이나 클릭하면 CPU Profiler가 열립니다.

CPU 사용량을 프로파일링해야 하는 이유

앱의 CPU 사용량을 최소화하면 더욱 빠르고 원활한 사용자 환경 제공 및 기기 배터리 수명 보존과 같은 많은 이점이 있습니다. 앱이 다양한 최신 기기와 기존 기기에서 잘 작동하는 데도 도움이 됩니다. CPU Profiler를 사용하여 앱과 상호작용하는 동안 CPU 사용량과 스레드 activity를 모니터링할 수 있습니다. 하지만 앱이 코드를 실행하는 방법에 대해 더 자세한 정보를 확인하려면 메서드 추적을 기록하고 검사해야 합니다.

앱 프로세스의 각 스레드에 대해 일정 기간 동안 실행되는 메서드와 실행 중에 각 메서드가 소비하는 CPU 리소스를 파악할 수 있습니다. 메서드 추적을 사용하여 호출자(caller)피호출자(callee)를 식별할 수도 있습니다. 호출자는 다른 메서드를 호출하는 메서드이고 피호출자는 다른 메서드에 의해 호출되는 메서드입니다. 이 정보를 사용하여 리소스를 많이 소비하는 특정 작업을 너무 자주 호출하는 원인이 되는 메서드를 확인하고 불필요한 작업을 방지하도록 앱의 코드를 최적화할 수 있습니다.

네이티브 시스템 프로세스를 검사하고 삭제된 프레임으로 인해 발생하는 UI 버벅거림 현상을 해결하는 데 도움이 되는 자세한 시스템 레벨 데이터를 수집하려면 systrace를 사용해야 합니다.

또는 Debug 클래스를 사용하여 캡처하는 .trace 파일을 내보내려면 Traceview를 사용해야 합니다.

CPU Profiler 개요

CPU Profiler를 열면 바로 시작되면서 앱의 CPU 사용량과 스레드 activity가 표시됩니다. 그림 1과 비슷한 화면이 나타날 것입니다.

그림 1. CPU Profiler.

그림 1에 표시된 것처럼, CPU Profiler의 기본 뷰에는 다음 사항이 포함됩니다.

  1. 이벤트 타임라인: 앱에서 수명 주기의 다양한 상태를 전환하며 이루어지는 activity를 보여주고, 화면 회전 이벤트를 비롯한 기기와의 사용자 상호작용을 표시합니다. 이벤트 타임라인 활성화 방법을 비롯해 이벤트 타임라인에 대한 자세한 내용은 고급 프로파일링 활성화를 읽어보세요.
  2. CPU 타임라인: 사용 가능한 총 CPU 시간 중의 비율로 표시되는 앱의 실시간 CPU 사용량과 앱에서 사용 중인 스레드의 총 개수를 보여줍니다. 이 타임라인은 다른 프로세스(예: 시스템 프로세스 또는 다른 앱)의 CPU 사용량도 보여주므로, 이를 자신이 개발한 앱의 사용량과 비교해 볼 수 있습니다. 타임라인의 수평축을 따라 마우스를 움직이면 CPU 사용량 내역 데이터를 살펴볼 수 있습니다.
  3. 스레드 activity 타임라인: 앱 프로세스에 속한 각각의 스레드를 나열하고 아래와 같은 색을 사용해 타임라인을 따라 activity를 표시합니다. 메서드 추적을 기록한 후 이 타임라인에서 스레드를 선택하여 추적 창에서 데이터를 살펴볼 수 있습니다.
    • 녹색: 스레드가 활성 상태이거나 CPU를 사용할 준비가 되었습니다. 즉, '실행 중' 또는 '실행 가능' 상태입니다.
    • 노란색: 스레드가 활성 상태이지만 디스크 또는 네트워크 I/O 같은 I/O 작업에서 대기 중이며, 대기 후에 작업을 완료할 수 있습니다.
    • 회색: 스레드가 슬립 모드인 상태로, CPU 시간을 소비하고 있지 않습니다. 이 현상은 스레드가 아직 사용할 수 없는 리소스에 액세스해야 할 때 가끔 발생합니다. 필요한 리소스를 사용할 수 있을 때까지는 스레드가 자발적으로 슬립 모드로 진입하거나 커널이 스레드를 슬립 모드로 전환합니다.
  4. 구성 기록: 다음 옵션 중 하나를 선택하여 프로파일러가 메서드 추적을 기록하는 방법을 결정할 수 있습니다.
    • Sampled: 앱 실행 중에 앱의 호출 스택을 잦은 간격으로 캡처하는 기본 구성입니다. 프로파일러는 캡처된 데이터 세트를 비교하여 앱의 코드 실행에 대한 타이밍 및 리소스 사용 정보를 얻습니다. 샘플링 기반 추적의 본질적 문제는 앱이 호출 스택 캡처 후에 메서드로 들어가거나 다음 캡처 이전에 메서드에서 나오는 경우 프로파일러가 해당 메서드 호출을 기록하지 않는다는 점입니다. 수명 주기가 이처럼 짧은 메서드를 추적하는 데 관심이 있다면 계측 추적을 사용해야 합니다.
    • 계측: 각 메서드 호출이 시작되고 끝날 때 타임스탬프 기록을 위해 런타임에서 앱을 계측하는 기본 구성입니다. 타이밍 정보와 CPU 사용량을 포함한 메서드 추적 데이터를 생성하기 위해 타임스탬프를 수집해서 비교합니다. 참고로, 각 메서드의 계측과 관련된 오버헤드는 런타임 성능에 영향을 주어 프로파일링 데이터에도 영향을 미칠 수 있습니다. 이 점은 수명 주기가 비교적 짧은 메서드에 대해 훨씬 더 현저히 드러납니다. 그 밖에도, 앱이 짧은 시간 내에 다수의 메서드를 실행하는 경우 프로파일러가 파일 크기 제한을 빠르게 초과하여 더 이상 추적 데이터를 기록하지 못할 수 있습니다.
    • 구성 편집: 앞서 설명한 샘플링 기록 구성과 계측 기록 구성의 특정 기본값을 변경하고 이를 사용자설정 구성으로 저장할 수 있습니다. 자세한 내용은 기록 구성 생성에 대한 섹션을 참조하세요.
  5. 기록 버튼: 메서드 추적 기록을 시작하고 중지하는 버튼입니다. 자세한 내용은 메서드 추적 기록 및 검사에 대한 섹션을 참조하세요.

참고: 이 프로파일러에서는 Android Studio와 Android 플랫폼이 앱 프로세스(예: JDWP, Profile Saver, Studio:VMStats, Studio:Perfa, Studio:Heartbeat)에 추가하는 스레드의 CPU 사용량도 보고합니다(단, 스레드 activity 타임라인에 표시되는 정확한 이름은 달라질 수 있음). 즉, CPU 타임라인에서 앱의 CPU 사용량에 이러한 스레드가 사용하는 CPU 시간도 표시된다는 뜻입니다. 스레드 activity 타임라인에서 이러한 스레드 중 일부를 확인하고 해당 activity를 모니터링할 수도 있습니다. (하지만 프로파일러 스레드가 네이티브 코드를 실행하므로 이들 스레드에 대한 메서드 추적 데이터는 기록할 수 없습니다.) Android Studio에서는 이 데이터를 보고하므로 개발자는 자신의 앱 코드 때문에 실제로 스레드 activity와 CPU 사용량이 발생할 때 이를 쉽게 파악할 수 있습니다.

메서드 추적 기록 및 검사

메서드 추적 기록을 시작하려면 Sampled 또는 Instrumented 기록 구성이나 직접 만든 사용자설정 기록 구성을 선택한 후 드롭다운 메뉴에서 Record 를 클릭하세요. 앱과 상호작용하고 필요한 작업을 마쳤으면 Stop recording 을 클릭하세요. 그러면 프로파일러가 기록된 시간 프레임을 자동으로 선택하여 그림 2와 같이 메서드 추적 창에 추적 정보를 표시합니다. 다른 스레드에 대한 메서드 추적을 검사하려면 스레드 activity 타임라인에서 바로 선택하면 됩니다.

그림 2. 메서드 추적을 기록한 후의 CPU Profiler.

  1. 선택한 시간 프레임: 추적 창에서 기록된 시간 프레임 중 검사하고 싶은 부분을 결정합니다. 메서드 추적을 처음 기록하면 CPU Profiler는 CPU 타임라인에서 기록의 전체 길이를 자동으로 선택합니다. 기록된 시간 프레임 중 일부에 대해서만 메서드 추적 데이터를 검사하고 싶으면 강조표시된 영역의 가장자리를 클릭하고 드래그하여 길이를 바꿀 수 있습니다.
  2. 타임스탬프: 기록된 메서드 추적의 시작 시간과 종료 시간을 표시합니다(프로파일러가 기기에서 CPU 사용 정보를 수집하기 시작한 시점을 기준으로 함). 타임스탬프를 클릭하면 전체 기록을 선택한 시간 프레임으로 자동으로 선택할 수 있는데, 이는 여러 개의 기록이 있고 이들 사이에서 전환하고 싶을 때 특히 유용합니다.
  3. 추적 창: 선택한 시간 프레임과 스레드에 대한 메서드 추적 데이터를 표시합니다. 이 창은 하나 이상의 메서드 추적을 기록한 후에만 나타납니다. 이 창에서 (추적 탭을 사용하여) 각 스택 추적을 어떤 방식으로 볼지, 그리고 (시간 기준 드롭다운 메뉴를 사용하여) 실행 시간을 어떤 방식으로 측정할지 선택할 수 있습니다.
  4. 메서드 추적을 Top Down 트리, Bottom Up 트리, Call Chart 또는 Flame Chart로 표시하도록 선택합니다. 아래 섹션에서 각 추적 창 탭에 대해 자세히 알아볼 수 있습니다.
  5. 드롭다운 메뉴에서 다음 옵션 중 하나를 선택하여 각 메서드 호출에 대한 타이밍 정보의 평가 방식을 결정합니다.
    • Wall clock time: 타이밍 정보가 실제 경과 시간을 나타냅니다.
    • Thread time: 타이밍 정보가 실제 경과 시간에서 스레드가 CPU 리소스를 소비하지 않는 시간을 뺀 시간을 나타냅니다. 임의의 메서드에 대해, 스레드 시간은 항상 wall clock time보다 짧거나 같습니다. 스레드 시간을 사용하면 스레드의 실제 CPU 사용량 중 특정 메서드가 소비한 시간이 얼마나 되는지 더욱 정확히 파악할 수 있습니다.

Call Chart 탭을 이용한 추적 검사

Call Chart 탭에는 메서드 호출(또는 호출자)의 기간 및 타이밍이 수평축에 표시되고 해당 피호출자가 수직축을 따라 표시되는 메서드 추적이 그래픽 형태로 제공됩니다. 시스템 API에 대한 메서드 호출은 주황색, 앱의 자체 메서드에 대한 호출은 녹색, 타사 API(Java 언어 API 포함)에 대한 메서드 호출은 파란색으로 표시됩니다. 아래 그림 3에는 호출 차트 예가 나와 있는데, 주어진 메서드에 대한 Self 시간, Children 시간, Total 시간의 개념이 묘사되어 있습니다. Top Down 및 Bottom Up을 이용한 추적 검사 방법에 관한 섹션에서 이러한 개념에 대해 자세히 알아보세요.

그림 3. 메서드 D에 대한 Self 시간, Children 시간, Total 시간을 보여주는 호출 차트 예.

팁: 메서드의 소스 코드로 이동하려면 메서드를 마우스 오른쪽 버튼으로 클릭하고 Jump to Source를 선택하세요. 이 기능은 추적 창 탭 중 어느 탭에서든 작동합니다.

Flame Chart 탭을 이용한 추적 검사

Flame Chart 탭에는 똑같은 호출 스택을 집계하는 반전 호출 차트가 제공됩니다. 즉, (호출 차트에 표시되는 것처럼 여러 개의 짧은 막대로 표시하는 것이 아니라) 동일한 호출자 시퀀스를 공유하는 똑같은 메서드가 수집되어 플레임 차트에서 하나의 긴 막대로 표시됩니다. 따라서 어떤 메서드가 가장 많은 시간을 소비하는지 더 쉽게 알 수 있습니다. 하지만 이는 수평축이 더 이상 타임라인을 나타내지 않는다는 의미로, 그 대신 각 메서드의 실행 소요 시간을 나타냅니다.

아래 그림 4에 나타낸 호출 차트를 생각해보면 이 개념을 좀 더 쉽게 이해할 수 있을 것입니다. 메서드 D는 B를 여러 차례 호출하는데(B1, B2, B3), 그중 일부는 C를 호출합니다(C1 및 C3).

그림 4. 공통된 호출자 시퀀스를 공유하는 여러 메서드 호출을 포함한 호출 차트.

B1, B2 및 B3는 동일한 호출자 시퀀스(A → D → B)를 공유하므로 아래 그림과 같이 집계됩니다. 마찬가지로, C1과 C3는 동일한 호출자 시퀀스(A → D → B → C)를 공유하기 때문에 집계됩니다. 여기서 C2는 호출자 시퀀스가 다르므로(A → D → C) 포함되지 않습니다.

그림 5. 동일한 호출 스택을 공유하는 동일한 메서드 집계.

집계된 메서드 호출은 그림 6과 같은 플레임 차트를 생성하는 데 사용됩니다. 참고로, 플레임 차트에 주어진 임의의 메서드 호출에 대해 가장 많은 CPU 시간을 소비하는 피호출자가 가장 먼저 나타납니다.

그림 6. 그림 4에 표시된 호출 차트의 플레임 차트 표시.

Top Down 및 Bottom Up을 이용한 추적 검사

Top Down 탭에는 메서드 노드를 확장할 때 피호출자가 표시되는 메서드 호출 목록이 표시됩니다. 그림 7은 그림 3의 호출 차트에 대한 하향식(top down) 그래프를 나타낸 것입니다. 그래프의 각 화살표가 호출자에서 피호출자로 향합니다.

그림 7에 나타낸 것처럼, 하향식 탭에서 메서드 A에 대한 노드를 확장하면 메서드 A의 피호출자인 메서드 B와 D가 표시됩니다. 그 후에 메서드 D에 대한 노드를 확장하면 메서드 D의 피호출자인 메서드 B와 C가 나타나는 식입니다. Flame chart 탭과 마찬가지로, 하향식 트리는 동일한 호출 스택을 공유하는 똑같은 메서드에 대한 추적 정보를 집계합니다. 즉, Flame chart 탭에 Top down 탭의 그래픽 표시가 제공됩니다.

Top Down 탭에는 각 메서드 호출에 사용되는 CPU 시간을 나타내는 데 도움이 되는 다음과 같은 정보가 제공됩니다(시간은 선택한 시간 프레임이 지속되는 동안 스레드의 총 시간에서 차지하는 비율로도 표시됨).

  • Self: 메서드 호출이 피호출자의 코드가 아니라 자체 코드를 실행하는 데 사용한 시간으로, 그림 3에 메서드 D에 대한 Self 시간이 표시되어 있습니다.
  • Children: 메서드 호출이 자체 코드가 아니라 피호출자의 코드를 실행하는 데 사용한 시간으로, 그림 3에 메서드 D에 대한 Children 시간이 표시되어 있습니다.
  • Total: 메서드의 Self 시간과 Children 시간을 합한 시간입니다. 이는 앱이 메서드 호출 실행에 사용한 총 시간을 나타내며, 역시 그림 3에 메서드 D에 대한 Total 시간이 표시되어 있습니다.

그림 7. 하향식 트리.

그림 8. 그림 7에서 메서드 C에 대한 상향식 트리.

Bottom Up 탭에는 메서드의 노드를 확장할 때 호출자가 표시되는 메서드 호출 목록이 표시됩니다. 그림 8은 그림 7의 예시 추적을 사용하여 메서드 C에 대한 상향식 트리를 제공합니다. 상향식 트리에서 메서드 C에 대한 노드를 열면 각각의 고유 호출자인 메서드 B와 D가 표시됩니다. 참고로, B가 C를 두 번 호출하더라도 상향식 트리에서 메서드 C에 대한 노드를 확장할 때 B는 한 번만 나타납니다. 그 후에 B에 대한 노드를 확장하면 B의 호출자인 메서드 A와 D가 표시됩니다.

Bottom Up 탭은 최대(또는 최소) CPU 시간을 소비하는 메서드를 기준으로 하는 메서드 정렬에 유용합니다. 각 노드를 검사하여 이러한 메서드를 호출하는 데 CPU 시간을 가장 많이 쓰는 호출자를 확인할 수 있습니다. 하향식 트리와 비교할 때, 상향식 트리에서 각 메서드에 대한 타이밍 정보는 각 트리의 맨 위(최상위 노드)에 있는 메서드를 기준으로 합니다. CPU 시간은 해당 기록 중에 스레드의 총 시간에서 차지하는 비율로도 나타냅니다. 다음 표는 최상위 노드와 호출자 메서드(하위 노드)에 대한 타이밍 정보의 해석 방법을 설명하는 데 도움이 됩니다.

Self Children Total
상향식 트리의 맨 위에 있는 메서드(최상위 노드) 메서드가 피호출자의 코드가 아니라 자체 코드의 실행에 사용한 총 시간을 나타냅니다. 하향식 트리와 비교해 볼 때, 이 타이밍 정보는 기록이 계속되는 시간 동안 이 메서드에 대한 모든 호출의 합을 나타냅니다. 메서드가 자체 코드가 아니라 피호출자 코드의 실행에 사용한 총 시간을 나타냅니다. 하향식 트리와 비교해 볼 때, 이 타이밍 정보는 기록이 계속되는 시간 동안 이 메서드의 피호출자에 대한 모든 호출의 합을 나타냅니다. Self 시간과 Children 시간의 합입니다.
호출자 메서드(하위 노드) 호출자가 호출할 때 피호출자의 총 Self 시간을 나타냅니다. 그림 8의 상향식 트리를 예로 사용할 때, 메서드 B에 대한 Self 시간은 B가 호출할 때 메서드 C의 각 실행을 위한 Self 시간의 합과 같습니다. 호출자가 호출할 때 피호출자의 총 Children 시간을 나타냅니다. 그림 8의 상향식 트리를 예로 사용하면, 메서드 B에 대한 Children 시간은 B가 호출할 때 메서드 C의 각 실행을 위한 Children 시간의 합과 같습니다. Self 시간과 Children 시간의 합입니다.

참고: 주어진 기록에 대해, 프로파일러가 파일 크기 제한에 도달할 때 Android Studio는 새 데이터 수집을 중지합니다(하지만 기록을 중지하는 것은 아님). 이런 일은 보통 계측 추적을 수행할 때 훨씬 더 빠르게 일어나는데, 그 이유는 샘플링 추적에 비해 이런 유형의 추적에서 더 짧은 시간 내에 더 많은 데이터를 수집하기 때문입니다. 제한 크기에 도달한 후 발생한 기록의 기간으로 검사 시간 프레임을 확장하는 경우 추적 창의 타이밍 데이터는 바뀌지 않습니다(새 데이터를 사용할 수 없기 때문). 뿐만 아니라, 기록에서 사용 가능한 데이터가 없는 부분만 선택하면 추적 창에는 타이밍 정보에 대해 NaN이 표시됩니다.

기록 구성 생성

Sampled 또는 Instrumented와 같이 Android Studio에서 제공되는 기록 구성 중 하나를 선택하거나 자체적으로 기록 구성을 생성할 수 있습니다. 사용자설정 구성을 생성 또는 편집하거나 기존의 기본 구성을 검사하려면 기록 구성 드롭다운 메뉴에서 Edit configurations를 선택하여 CPU Recording Configurations_ 대화상자를 엽니다.

그림 9. CPU Recording Configurations 대화상자를 통해 사용자설정 기록 구성을 생성 또는 편집하거나 기존의 기본 구성을 검사할 수 있습니다.

다음과 같이 왼쪽 창에서 기존 구성을 선택하여 그 설정을 살펴보거나 새 기록 구성을 생성할 수 있습니다.

  1. 대화상자의 상단 왼쪽에서 Add 를 클릭합니다. 그러면 몇 가지 기본 설정으로 새 구성이 생성됩니다.
  2. 구성의 이름을 지정합니다.
  3. Trace Technology 섹션에서 Sampled 또는 Instrumented를 선택합니다. 이들 옵션은 각각 CPU Profiler 개요의 설명과 같이 작동합니다.
  4. 샘플링 기록 구성의 경우 Sampling interval을 마이크로초(μs) 단위로 지정합니다. 이 값은 앱 호출 스택의 각 샘플 간 지속 시간을 나타냅니다. 이 시간을 짧게 지정할수록 기록되는 데이터에 대한 파일 크기 제한에 빠르게 도달하게 된다는 점에 유의하세요.
  5. 연결된 기기에 작성되는 기록된 데이터의 File size limit(MB)를 지정합니다. 기록을 중지하면 Android Studio가 이 데이터를 파싱하여 프로파일러 창에 표시합니다. 그래서 제한을 늘려 대량의 데이터를 기록하면 Android Studio가 파일을 파싱하는 데 훨씬 더 오래 걸리고 아예 응답하지 못하게 될 수도 있습니다.

    참고: API 레벨 26 이상을 실행 중인 연결된 기기를 사용하는 경우 추적 데이터의 파일 크기에 대한 제한이 없고 이 값은 무시됩니다. 하지만 Android Studio가 큰 추적 파일을 파싱하기 어려울 수 있으므로 각각의 기록 후에 기기가 수집하는 데이터의 양에 계속 주의를 기울일 필요가 있습니다. 예를 들어, 앱이 짧은 시간 내에 많은 메서드를 호출하는 동안 샘플링 간격이 짧은 샘플링 추적 또는 계측 추적을 기록할 경우 큰 추적 파일들이 빠르게 생성됩니다.

  6. Apply 또는 OK를 클릭합니다. 다른 기록 구성을 변경한 경우 해당 변경 사항도 적용됩니다.

새 구성을 생성하고 나면 기록 구성 드롭다운 메뉴에서 새 구성이 자동으로 선택되며 이를 다음 기록에 사용할 수 있습니다.