[성능 최적화] Perfetto를 사용하여 애플리케이션 시작 성능의 병목 현상 찾기

많은 사람들이 Android 애플리케이션 시작 최적화와 관련된 기사를 작성했지만 주로 시작 성능에 어떤 변화가 있었는지에 초점을 맞추고 있으며 애플리케이션의 시작 성능을 분석하고 식별하는 방법에 대해 설명하는 기사는 거의 없습니다.

이 기사에서는 Perfetto를 사용한 개인적인 경험을 결합하여 차량 내 애플리케이션의 시작 시간을 측정하는 방법을 설명하고, 시작 시간을 측정한 후 성능 병목 현상을 어떻게 분석할 수 있는지 설명합니다.

애플리케이션 시작 성능을 분석하기 전에 먼저 Android의 애플리케이션 시작 시간에 대한 몇 가지 기본 상식을 간략하게 이해합니다.

애플리케이션 시작 시간

초기 표시 시간(TTID)

시스템이 시작 의도를 수신한 후 인터페이스의 첫 번째 프레임을 표시하는 애플리케이션까지의 시간인 TTID(Time to Initial Display)는 사용자가 애플리케이션 인터페이스를 보는 시간입니다.

TTID 측정

애플리케이션이 위에서 언급한 모든 작업을 완료하면 logcat에서 다음 로그 출력을 볼 수 있습니다.

/system_process I/ActivityTaskManager: Displayed xxxx/.MainActivity: +401ms

모든 리소스가 완전히 로드되어 표시되기 전에 Logcat 출력에 시간 측정항목을 표시하고 레이아웃 파일에서 참조되지 않은 리소스나 앱이 객체 초기화의 일부로 리소스를 생성하는 시간을 생략했습니다.

때로는 logcat 출력의 로그에 추가 필드 합계가 포함되는 경우가 있습니다. 다음과 같이:

ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms (total +1m22s643ms)

이 경우 첫 번째 측정은 그려진 첫 번째 활동에 대해서만 적용됩니다. total시간 측정은 앱이 시작될 때부터 측정되며 처음 시작되지만 화면에 아무것도 표시하지 않는 다른 활동을 포함할 수 있습니다. total시간 측정은 개별 활동 시간과 총 시작 시간 사이에 차이가 있는 경우에만 표시됩니다.

일부 블로그에 소개된 명령어를 사용하여 am start -S -W측정한 시간은 실제로는 초기 표시 시간입니다. 대부분의 경우 초기 표시 시간은 애플리케이션의 실제 시작 시간을 나타내지 않습니다.예를 들어, 애플리케이션이 시작되면 네트워크의 최신 데이터를 동기화해야 합니다.실제 시작 시간은 모든 응용 프로그램이 시작될 때 걸리는 시간입니다. 다음번에 소개할 TTFD - Time Total Display 입니다 .

전체 표시 시간(TTFD)

전체 표시 시간(TTFD, 전체 표시 시간). 시스템이 실행 인텐트를 수신한 시점부터 애플리케이션이 모든 리소스 및 보기 계층 로드를 완료한 시점, 즉 사용자가 실제로 애플리케이션을 사용할 수 있는 시점까지의 시간입니다.

TTFD 측정

방법 1: 전화reportFullyDrawn()

reportFullyDrawn()애플리케이션이 모든 리소스 로드와 계층 구조 보기를 완료한 후 메서드를 호출하여 전체 표시 시간을 계산할 수 있도록 애플리케이션이 완전히 표시되었음을 시스템에 알릴 수 있습니다. 이 메소드가 호출되지 않으면 시스템은 TTID만 계산할 수 있고 TTFD는 계산할 수 없습니다.

system_process I/ActivityTaskManager: Fully drawn xxxx/.MainActivity: +1s54ms

방법 2: 프레임 해제

프레임 분할 방법은 현재 차량 내 애플리케이션의 시작 시간을 계산하는 가장 일반적인 방법입니다. 프레임 분할 방법에는 다양한 녹화 및 프레임 분할 방법이 있습니다.

Potplay60fps를 지원하는 카메라(60fps 카메라를 지원하는 휴대폰도 사용 가능)를 사용하여 애플리케이션의 시작 영상을 촬영한 후, 데스크탑 클릭 과 프레임 수의 차이를 확인하기 위해 비디오 플레이어를 사용하는 것이 일반적입니다. 애플리케이션 화면이 완전히 표시되도록 한 다음 60으로 나눕니다. 애플리케이션의 시작 시간을 얻을 수 있습니다.

위의 방법은 테스터에게 적합하지만 개발자에게 더 적합한 또 다른 방법은 FFmpeg 프레임 분할입니다.

FFmpeg 다운로드 주소: http://ffmpeg.org/download.html?aemtn=tg-on

먼저 adb를 사용하여 Android 기기에 연결하고 화면 녹화 명령을 사용하여 애플리케이션이 시작될 때 비디오를 녹화합니다.

adb shell screenrecord /sdcard/launch.mp4

FFmpeg를 사용하여 비디오의 프레임 번호 확인

ffmpeg -i launch.mp4 

비디오 프레임 수가 60fps 미만인 경우 계속해서 FFmpeg를 사용하여 비디오에 프레임을 60fps로 추가합니다.

ffmpeg -i launch.mp4 -filter:v fps=60 output.mp4

프레임이 채워진 비디오의 각 프레임을 그림으로 분할한 다음 바탕 화면을 클릭할 때부터 응용 프로그램 화면이 완전히 표시될 때까지 의 프레임 수의 차이를 계산합니다.

ffmpeg -i output.mp4 output_%04d.jpg

또는 프레임이 채워진 비디오를 애니메이션 GIF로 변환하고 사진 브라우저를 사용하여 프레임 수를 계산합니다(MAC OS에 내장된 사진 브라우저가 수행함).

ffmpeg -i output.mp4 -vf fps=60,scale=320:-1:flags=lanczos -loop 0 output.gif

애플리케이션 시작 시간 및 측정 방법에 대한 소개입니다. 자세한 내용은 Android 공식 문서 " 애플리케이션 시작 시간 | 애플리케이션 품질 | Android "를 참조하세요.

현재 주류 차량 애플리케이션의 평균 시작 시간(예: 8155 플랫폼)은 다음과 같습니다.

  • 콜드 스타트 ​​TTFD

타사 대규모 인터넷 애플리케이션은 2.6초 미만으로 제어해야 하며 차량 시스템 애플리케이션은 1.6초 미만으로 제어해야 합니다.

  • 웜 스타트 TTFD

일반적으로 0.8초 이하로 제어해야 합니다.

위 내용은 개인적인 경험으로 호스트 제조업체마다 성능 요구 사항이 다릅니다.

퍼페토 소개

Perfetto안드로이드 10에 도입된 시스템 수준의 추적 도구로 안드로이드, 리눅스, 크롬을 지원하며 Systrace. Profiler와 비교하면 AGI더 이상 애플리케이션에만 국한되지 않고 전체 시스템의 실행 상태를 제공할 수 있습니다. 애플리케이션이 시스템의 안정성과 유창성에 영향을 미치는지 확인해야 하거나 반대로 분석하는 데 사용할 수 있습니다. 시스템이 애플리케이션에 미치는 영향 실행 중인 영향이 감지되면 Perfetto시스템 수준 추적 및 분석에 .

Perfetto의 기본 콘텐츠는 제가 이전에 번역한 공식 Android 동영상을 확인하세요. [번역] 최신 Android 개발 기술 - Perfetto 시작하기

Perfetto로 빠르게 시작해보세요

Perfetto사용하는 방법은 다양하며, 개인적으로는 record_android_trace스크립트를 사용하는 것을 추천합니다. Perfettoadb를 사용하여 Android 기기에서 성능 데이터를 수집하는 데 도움을 주기 위해 제공되는 보조 스크립트 입니다 . 이 스크립트는 다음을 수행합니다.

  • perfetto장치에서 바이너리를 사용할 수 있는지 자동으로 감지하고 , 그렇지 않은 경우 GitHub에서 다운로드하여 장치에 푸시하려고 시도합니다.
  • 추적 시간, 버퍼 크기, 출력 파일 경로 등과 같은 추적 구성 매개변수를 자동으로 설정합니다.
  • 추적이 완료된 후 자동으로 명령을 실행 perfetto하고 출력 파일을 컴퓨터로 가져옵니다.
  • 브라우저에서 출력 파일을 자동으로 열어 추적 결과를 보고 분석할 수 있습니다.

Record_android_trace 다운로드 주소: https://raw.githubusercontent.com/google/perfetto/master/tools/record_android_trace

record_android_trace사용법은 다음과 같습니다.

./record_android_trace [options] [category1] [category2] ...

그중 options 에는 다음과 같은 선택적 매개변수가 있습니다.

  • -o OUT_FILE: 출력 파일의 경로를 지정합니다. 지정하지 않은 경우 기본값은 perfetto_trace.pb입니다.
  • -t TIME : 추적 시간을 지정하며, 지정하지 않을 경우 기본값은 10초이다.
  • -b SIZE: 추적 버퍼 크기를 지정합니다. 지정하지 않을 경우 기본값은 32MB입니다.

Category 는 추적할 atrace 또는 ftrace 범주입니다. –list를 사용하면 장치에서 지원하는 Trace 범주를 볼 수 있습니다. 출력은 다음과 같습니다.

link@link-PC:~/Desktop$ ./record_android_trace --list
         gfx - Graphics
       input - Input
        view - View System
     webview - WebView
          wm - Window Manager
          am - Activity Manager
          sm - Sync Manager
       audio - Audio
       video - Video
      camera - Camera
         hal - Hardware Modules
         res - Resource Loading
      dalvik - Dalvik VM
          rs - RenderScript
      bionic - Bionic C Library
       power - Power Management
          pm - Package Manager
          ss - System Server
    database - Database
     network - Network
         adb - ADB
    vibrator - Vibrator
        aidl - AIDL calls
       nnapi - NNAPI
         rro - Runtime Resource Overlay
         pdx - PDX services
       sched - CPU Scheduling
         irq - IRQ Events
         i2c - I2C Events
        freq - CPU Frequency
        idle - CPU Idle
        disk - Disk I/O
        sync - Synchronization
       workq - Kernel Workqueues
  memreclaim - Kernel Memory Reclaim
  regulators - Voltage and Current Regulators
  binder_driver - Binder Kernel driver
  binder_lock - Binder global lock trace
   pagecache - Page cache
      memory - Memory
     thermal - Thermal event
         gfx - Graphics (HAL)
         ion - ION allocation (HAL)

예를 들어, sched, gfx 및 view를 추적하려는 경우 출력 파일은 Trace.perfetto-trace이고 추적 시간은 5초이며 버퍼 크기는 16MB인 경우 다음 명령을 실행할 수 있습니다.

./record_android_trace -o trace.perfetto-trace -t 5s -b 16mb sched gfx view

Perfetto는 시작 성능을 분석합니다.

Perfetto를 사용하여 애플리케이션의 시작 성능을 분석하는 것은 매우 간단합니다. 먼저, Record_android_trace를 사용하여 애플리케이션의 시작 데이터를 캡처하고 다음 지침을 실행합니다.

./record_android_trace -o trace.perfetto-trace -t 15s -b 200mb gfx input view webview wm am sm audio video camera hal res dalvik rs bionic power pm ss database network adb vibrator aidl nnapi rro pdx sched irq i2c freq idle disk sync workq memreclaim regulators binder_driver binder_lock pagecache memory gfx ion

15초 후에 Record_android_trace가 자동으로 브라우저를 엽니다. 한 열에 Android App Startups표시된 것은 애플리케이션의 시작 시간입니다. 아래와 같이 표시 Android App Startups되는 시간은 애플리케이션의 TTID , 즉 초기 표시 시간 이라는 점에 유의하는 것이 중요합니다 .

Perfetto 왼쪽에서 Metrics를 선택한 다음 android_startup을 선택 하고 Run을 클릭하면 아래와 같이 Perfetto가 애플리케이션이 시작될 때 자동으로 다양한 데이터를 분석하는 데 도움이 됩니다.

android_startup {
  startup {
    startup_id: 1
    startup_type: "warm"
    package_name: "com.xxx.xxx.weather"
    process_name: "com.xxx.xxx.weather"
    process {
      name: "com.xxx.xxx.weather"
      uid: 1000
      pid: 3376
    }
    zygote_new_process: false
    activity_hosting_process_count: 1
    event_timestamps {
      intent_received: 100680138137
      first_frame: 102167532928
    }
    to_first_frame {
      dur_ns: 1487394791
      dur_ms: 1487.394791
      main_thread_by_task_state {
        running_dur_ns: 1316606193
        runnable_dur_ns: 34121303
        uninterruptible_sleep_dur_ns: 20429636
        interruptible_sleep_dur_ns: 84415940
        uninterruptible_io_sleep_dur_ns: 12221457
        uninterruptible_non_io_sleep_dur_ns: 8208179
      }
      time_activity_manager {
        dur_ns: 16070209
        dur_ms: 16.070209
      }
      time_activity_start {
        dur_ns: 97578437
        dur_ms: 97.578437
      }
      time_activity_resume {
        dur_ns: 833413073
        dur_ms: 833.413073
      }
      time_choreographer {
        dur_ns: 481555469
        dur_ms: 481.555469
      }
      time_inflate {
        dur_ns: 1241538748
        dur_ms: 1241.538748
      }
      time_get_resources {
        dur_ns: 6173178
        dur_ms: 6.173178
      }
      time_verify_class {
        dur_ns: 1675365
        dur_ms: 1.675365
      }
      time_gc_total {
        dur_ns: 82049531
        dur_ms: 82.049531
      }
      time_dlopen_thread_main {
        dur_ns: 15522344
        dur_ms: 15.522344
      }
      time_lock_contention_thread_main {
        dur_ns: 4711976
        dur_ms: 4.711976
      }
      time_jit_thread_pool_on_cpu {
        dur_ns: 375033124
        dur_ms: 375.033124
      }
      time_gc_on_cpu {
        dur_ns: 81314427
        dur_ms: 81.314427
      }
      jit_compiled_methods: 218
      other_processes_spawned_count: 6
    }
    verify_class {
      name: "com.xxx.xxx.weather.service.VoiceActionManager"
      dur_ns: 1675365
    }
    dlopen_file: "/system/priv-app/Weather/Weather.apk!/lib/arm64-v8a/libffavc.so"
    dlopen_file: "/system/priv-app/Weather/Weather.apk!/lib/arm64-v8a/libpag.so"
    dlopen_file: "/vendor/lib64/hw/[email protected]"
    dlopen_file: "libadreno_utils.so"
    dlopen_file: "/vendor/lib64/hw/[email protected]"
    dlopen_file: "/vendor/lib64/hw/gralloc.msmnile.so"
    dlopen_file: "libadreno_app_profiles.so"
    dlopen_file: "libEGL_adreno.so"
    system_state {
      dex2oat_running: false
      installd_running: false
      broadcast_dispatched_count: 0
      broadcast_received_count: 0
      most_active_non_launch_processes: "media.codec"
      most_active_non_launch_processes: "app_process"
      most_active_non_launch_processes: "media.hwcodec"
      most_active_non_launch_processes: "/vendor/bin/hw/vendor.qti.hardware.display.allocator-service"
      most_active_non_launch_processes: "/system/bin/audioserver"
      installd_dur_ns: 0
      dex2oat_dur_ns: 0
    }
slow_start_reason: "GC Activity"
slow_start_reason: "Main Thread - Time spent in Running state"
slow_start_reason: "Time spent in view inflation"
  }
}

android_startup 은 안드로이드 애플리케이션의 시작 성능을 기록하고 분석하는 데 사용되는 데이터 구조로, 시작 유형, 시작 시간, 시작 이유, 시작 종속성, 시스템 상태 등 애플리케이션 시작 프로세스 중 다양한 정보가 포함되어 있습니다.

android_startup 의 콘텐츠 는 com.xxx.xxx.weather라는 날씨 애플리케이션의 시작 데이터를 나타내는 protobuf 형식의 텍스트입니다. 그 중 가장 중요한 것은 마지막 단락의 Slow_start_reason 으로, 애플리케이션이 느린 시작을 유발할 수 있는 이유를 보여주며, 세 번째 섹션에서 분석에 중점을 두겠습니다.

다른 필드의 의미는 다음과 같습니다.

start_id: 첫 번째 시작임을 나타내는 고유 식별자입니다.

start_type: 열거 유형으로, 웜 스타트업, 즉 애플리케이션 프로세스가 이미 존재하지만 포그라운드에 활동이 없음을 나타냅니다.

package_nameprocess_name은 애플리케이션 패키지 이름과 프로세스 이름을 나타냅니다.

프로세스: 이름, 사용자 식별자(uid) 및 프로세스 식별자(pid)를 포함하여 애플리케이션 프로세스에 대한 정보를 나타냅니다.

zygote_new_process: zygote를 통해 새 프로세스가 생성되는지 여부를 나타냅니다. 여기서는 false입니다.

Activity_hosting_process_count: 이 프로세스에서 호스팅되는 활동 수를 나타냅니다. 여기서는 1입니다.

event_timestamps: 다양한 이벤트의 타임스탬프를 나타냅니다.예를 들어,intent_received는 시작 의도가 수신된 시간을 나타내고,first_frame은 첫 번째 프레임이 표시되는 시간을 나타냅니다.

to_first_frame: 총 시간, 메인 스레드의 다양한 상태 시간, 다양한 작업 시간, 다양한 리소스 사용량 등을 포함하여 시작 의도를 수신한 후 첫 번째 프레임을 표시할 때까지의 시간 및 세부 정보를 나타냅니다.

verify_class: 클래스 이름, 시간 등 클래스 로딩 확인에 대한 정보를 나타냅니다.

dlopen_file: 파일 이름을 포함하여 공유 라이브러리 파일 열기에 대한 정보를 나타냅니다.

system_state: dex2oat 또는 installd가 실행 중인지 여부, 브로드캐스트 전송 또는 수신 여부, 가장 활동적인 비시작 프로세스 등을 포함한 시스템 상태를 나타내는 정보.

퍼페토 연습

시작 시 GC 트리거

현상 : "GC 활동"이 Slow_start_reason 에 나타나며, 이는 GC 활동으로 인해 시작 단계에서 애플리케이션 시작 속도가 느려짐을 나타냅니다.

분석 : [타임라인 표시]를 클릭하면 Perfetto 타임라인 인터페이스로 돌아갑니다. 시작 타임라인을 보면 애플리케이션의 GC 스레드인 HeapTaskDaemon 이라는 스레드가 시작 단계에서 약 100ms 동안 활성화되어 ActivityResume 타임라인이 100ms 연장된 것을 확인할 수 있습니다 . 이러한 현상이 우연히 발견되는 것을 방지하기 위해 여러 번 측정을 수행한 결과 애플리케이션이 시작되면 반드시 GC 활동이 트리거된다는 사실을 발견했습니다. 그림과 같이:

이유 : 타임라인의 전방 분석 결과, 애플리케이션이 시작 단계에서 특수 글꼴을 로드하는 것으로 나타났습니다. 글꼴 크기는 약 13MB입니다. 애플리케이션 개발팀과 연락한 후 해당 글꼴이 다음 위치로 이동된 것으로 확인되었습니다. 시스템 계층과 응용 프로그램 계층은 글꼴을 로드할 필요가 없습니다. 글꼴을 제거한 후 애플리케이션이 시작될 때 GC가 더 이상 100% 트리거되지 않습니다.

메인 스레드에서 시간이 많이 걸리는 작업

현상 : "메인 스레드 - 실행 상태에서 소요된 시간"이 Slow_start_reason 에 나타납니다 . 이는 시작 단계 동안 더 많은 시간이 소요되는 작업이 메인 스레드에서 수행됨을 의미합니다.

이유 : 이 상황은 애플리케이션 개발 중에 매우 일반적입니다. 애플리케이션 개발자는 자연스럽게 기본 스레드 활동의 OnCreate 또는 onStart 메소드 아래에 일부 크로스 프로세스 데이터 수집 작업을 배치합니다. 이러한 IPC 메소드는 ANR을 트리거하지 않지만 시작 속도를 늦추게 됩니다. 애플리케이션이며 실행을 위해 스레드 풀이나 코루틴에 배치되어야 합니다.

OpenDexFilesFromOat에는 시간이 걸립니다.

현상 : "메인 스레드 - OpenDexFilesFromOat*에서 소요된 시간"이 Slow_start_reason 에 나타나 시작 단계에서 dex 파일을 읽는 데 더 많은 시간이 소요됨을 나타냅니다.

이유 : 이 상황은 자동차 Android 시스템에서 더 일반적입니다. 이는 시스템이 시작 속도를 높이기 위해 시스템 내 dex2oat 프로세스를 수정했기 때문일 수 있으며, 이러한 현상이 발생하는 경우도 있지만, 시간이 많이 걸리지 않는다면 무시해도 됩니다.

여기서 그냥 '가능하다'고 말한 이유는 오늘날의 자동차 OS는 빠른 시작을 위해 기본 안드로이드에 많은 수정을 가했기 때문이며, 우리 자신의 실제 상황을 바탕으로 상세한 분석을 해야 한다.

연속 다중 프레임 그리기 시간 초과

현상: Perfetto에서 애플리케이션의 시작 시간은 약 1.3초로 길지 않습니다. 그러나 프레임 분할 방법을 사용한 후에는 애플리케이션이 시작 후 약간 지연되어 실제 시작 시간이 2.1초로 연장되는 것으로 나타났습니다. 에스. Perfetto의 성능은 다음과 같습니다.첫 번째 프레임을 그린 후 이어지는 2, 4, 5프레임의 드로잉 시간이 150ms를 초과합니다.

분석 : Perfetto가 제공한 프레임 그리기 타임라인은 대부분의 시간이 뷰의 레이아웃에서 소비됨을 보여줍니다. 이는 첫 번째 프레임이 그려진 후 여러 페이지 다시 그리기가 트리거됨을 보여줍니다.

이유 : 코드와 애플리케이션 로그를 결합한 결과 애플리케이션이 시작될 때 빈 데이터로 페이지를 한 번 새로 고친 다음 IPC 인터페이스에서 데이터를 얻어 페이지를 다시 업데이트하는 것으로 나타났습니다. 새로고침이 4번 연속으로 실행되어 이런 상황이 발생합니다. 첫 번째 프레임 이후 연속 그리기 시간 초과가 더 이상 발생하지 않도록 결함이 있는 코드를 수정합니다.

참고자료

https://developer.android.com/topic/performance/vitals/launch-time#time-initial

추천

출처blog.csdn.net/linkwj/article/details/132460341