[Optimización del rendimiento] Utilice Perfetto para localizar cuellos de botella en el rendimiento del inicio de la aplicación

Mucha gente ha escrito artículos relacionados con la optimización del inicio de aplicaciones de Android, pero se centran principalmente en los cambios que se han realizado en el rendimiento de inicio, y pocos artículos hablan sobre cómo analizar e identificar el rendimiento de inicio de las aplicaciones.

Este artículo combinará mi experiencia personal con Perfetto para explicar cómo se mide el tiempo de inicio de las aplicaciones en el vehículo y, después de medir el tiempo de inicio, ¿cómo podemos analizar los cuellos de botella en el rendimiento?

Antes de analizar el rendimiento de inicio de la aplicación, primero comprendamos brevemente algo de sentido común básico sobre el tiempo de inicio de la aplicación en Android.

Hora de inicio de la aplicación

Hora de visualización inicial (TTID)

El tiempo hasta la visualización inicial (TTID), que es el tiempo desde que el sistema recibe la intención de inicio hasta que la aplicación muestra el primer cuadro de la interfaz, es el momento en que el usuario ve la interfaz de la aplicación.

Medir TTID

Cuando la aplicación completa todo el trabajo mencionado anteriormente, se puede ver el siguiente resultado de registro en logcat.

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

Se muestran métricas de tiempo en la salida de Logcat antes de que todos los recursos se carguen y muestren por completo, omitiendo los recursos a los que no se hace referencia en el archivo de diseño o el momento en que la aplicación crea recursos como parte de la inicialización del objeto.

A veces, los registros en la salida de logcat contendrán un total de campos adicional. Como sigue:

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

En este caso, la primera medición es sólo para la primera actividad sorteada. totalLa medición del tiempo se mide desde que se inicia la aplicación, y puede incluir otra actividad que se inicia por primera vez pero que no muestra nada en pantalla. totalLas mediciones de tiempo solo se muestran si hay una diferencia entre el tiempo de la actividad individual y el tiempo total de inicio.

am start -S -WEl tiempo medido mediante los comandos introducidos en algunos blogs es en realidad el tiempo de visualización inicial. En la mayoría de los casos, el tiempo de visualización inicial no representa el tiempo de inicio real de la aplicación. Por ejemplo, cuando se inicia la aplicación, necesita sincronizar los datos más recientes de la red. El tiempo de inicio real es el tiempo que lleva cuando todos los Se cargan los datos. Este es el TTFD que se introducirá la próxima vez: Visualización total .

Tiempo hasta la visualización completa (TTFD)

El tiempo hasta la visualización completa (TTFD, The Time to Full Display). Es el momento desde que el sistema recibe la intención de inicio hasta que la aplicación termina de cargar todos los recursos y ver las jerarquías, es decir, cuando el usuario realmente puede usar la aplicación.

Medida TTFD

Método 1: llamarreportFullyDrawn()

reportFullyDrawn()Se puede llamar al método después de que la aplicación haya terminado de cargar todos los recursos y ver las jerarquías, lo que le permite al sistema saber que la aplicación se ha mostrado por completo para que se pueda calcular el tiempo de visualización completo. Si no se llama a este método, el sistema solo puede calcular el TTID pero no el TTFD.

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

Método 2: desenmarcar

El método de división de cuadros es actualmente el método más común para calcular el tiempo de inicio de las aplicaciones en el vehículo. El método de división de cuadros tiene muchos métodos diferentes de grabación y división de cuadros.

Es común usar una cámara que admita 60 fps (también se pueden usar teléfonos móviles que admitan cámaras de 60 fps) para grabar el video de inicio de la aplicación y luego usar el reproductor de video para ver la diferencia en la cantidad de cuadros desde el clic del escritorioPotplay . hasta que la pantalla de la aplicación se muestre completamente y luego divídala por 60. Puede obtener el tiempo de inicio de la aplicación.

El método anterior es adecuado para los evaluadores, aquí hay otro método que es más adecuado para los desarrolladores: la división de fotogramas FFmpeg.

Dirección de descarga de FFmpeg: http://ffmpeg.org/download.html?aemtn=tg-on

Primero use adb para conectarse al dispositivo Android y use el comando de grabación de pantalla para grabar el video cuando se inicie la aplicación.

adb shell screenrecord /sdcard/launch.mp4

Utilice FFmpeg para comprobar el número de fotograma de un vídeo

ffmpeg -i launch.mp4 

Si la cantidad de cuadros de video es inferior a 60 fps, continúe usando FFmpeg para agregar cuadros al video a 60 fps.

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

Divida cada cuadro del video lleno de cuadros en una imagen y luego calcule la diferencia en la cantidad de cuadros desde que hace clic en el escritorio hasta que se muestra completamente la pantalla de la aplicación .

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

O convierta el vídeo lleno de fotogramas en un GIF animado y utilice un navegador de imágenes para contar los fotogramas (el navegador de imágenes integrado de MAC OS servirá).

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

Esta es una introducción al tiempo de inicio de la aplicación y los métodos de medición. Para obtener más información, consulte el documento oficial de Android " Tiempo de inicio de la aplicación | Calidad de la aplicación | Android ", que es muy detallado.

Vale la pena mencionar que el tiempo de inicio promedio actual de las aplicaciones de vehículos convencionales (tomando la plataforma 8155 como ejemplo) es el siguiente:

  • Arranque en frío TTFD

Las aplicaciones de Internet a gran escala de terceros deben controlarse por debajo de 2,6 segundos y las aplicaciones de sistemas de vehículos deben controlarse por debajo de 1,6 segundos.

  • Arranque en caliente TTFD

Generalmente es necesario controlarlo por debajo de 0,8 s.

Lo anterior es mi experiencia personal: diferentes fabricantes de hosts definitivamente tendrán diferentes requisitos de rendimiento.

Introducción al Perfecto

PerfettoEs una herramienta de seguimiento a nivel de sistema introducida en Android 10, compatible con Android, Linux y Chrome, y se utiliza para reemplazar Systrace. En comparación con Profilery AGI, ya no se limita a la aplicación, sino que puede proporcionar el estado de ejecución de todo el sistema. Cuando necesitamos ver si la aplicación afecta la estabilidad y fluidez del sistema, o por el contrario, se puede utilizar para analizar el impacto del sistema en la aplicación. Se puede utilizar Perfettopara el seguimiento y análisis a nivel del sistema .

Para conocer el contenido básico de Perfetto, puede ver el video oficial de Android que traduje antes: [Traducción] Habilidades modernas de desarrollo de Android: introducción a Perfetto

Comience rápidamente con Perfetto

PerfettoHay muchas formas de usarlo y personalmente recomiendo usar record_android_tracescripts. Es Perfettoun script auxiliar proporcionado para ayudarnos a recopilar datos de rendimiento de dispositivos Android mediante adb. Este script hace lo siguiente:

  • Detecta automáticamente si perfettoel binario está disponible en el dispositivo y, si no, intenta descargarlo desde GitHub y enviarlo al dispositivo.
  • Establezca automáticamente los parámetros de configuración de seguimiento, como el tiempo de seguimiento, el tamaño del búfer, la ruta del archivo de salida, etc.
  • Ejecute automáticamente perfettoel comando y extraiga el archivo de salida a su computadora una vez que se complete el seguimiento.
  • Abre automáticamente archivos de salida en un navegador, lo que le permite ver y analizar los resultados del seguimiento.

Dirección de descarga de record_android_trace: https://raw.githubusercontent.com/google/perfetto/master/tools/record_android_trace

record_android_traceEl uso es el siguiente:

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

Entre ellas, las opciones son algunos parámetros opcionales, como:

  • -o OUT_FILE: especifique la ruta del archivo de salida. Si no se especifica, el valor predeterminado es perfetto_trace.pb.
  • -t TIME: especifique el tiempo de seguimiento. Si no se especifica, el valor predeterminado es 10 segundos.
  • -b TAMAÑO: especifica el tamaño del búfer de seguimiento. Si no se especifica, el valor predeterminado es 32 MB.

La categoría son algunas categorías atrace o ftrace que se van a rastrear. Puede usar –list para ver las categorías de seguimiento admitidas por el dispositivo. El resultado puede ser el siguiente:

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)

Por ejemplo, si desea rastrear programación, gfx y ver, el archivo de salida es trace.perfetto-trace, el tiempo de rastreo es de 5 segundos y el tamaño del búfer es de 16 MB, puede ejecutar el siguiente comando:

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

Perfetto analiza el rendimiento de las startups

Es muy sencillo utilizar Perfetto para analizar el rendimiento de inicio de la aplicación. Primero, utilice record_android_trace para capturar los datos de inicio de la aplicación y ejecutar las siguientes instrucciones:

./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

Después de 15 segundos, record_android_trace nos abrirá automáticamente el navegador. En una columna se Android App Startupsmuestra la hora de inicio de la aplicación. Como se muestra a continuación, es importante tener en cuenta que Android App Startupsla hora que se muestra es el TTID de la aplicación : la hora de visualización inicial .

Seleccione Métricas en el lado izquierdo de Perfetto, luego seleccione android_startup , haga clic en Ejecutar, Perfetto nos ayudará automáticamente a analizar varios datos cuando se inicie la aplicación, como se muestra a continuación.

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 es una estructura de datos que se utiliza para registrar y analizar el rendimiento de inicio de las aplicaciones de Android y contiene diversa información durante el proceso de inicio de la aplicación, como tipo de inicio, hora de inicio, motivo de inicio, dependencias de inicio, estado del sistema, etc.

El contenido de android_startup es un texto en formato protobuf, que representa los datos de inicio de una aplicación meteorológica llamada com.xxx.xxx.weather. Entre ellos, el más importante es slow_start_reason en el último párrafo , que nos muestra las razones por las que la aplicación puede provocar un inicio lento, nos centraremos en el análisis en la tercera sección.

Los significados de otros campos son los siguientes:

startup_id: es un identificador único que indica que se trata del primer inicio;

startup_type: es un tipo de enumeración, lo que indica que se trata de un inicio en caliente, es decir, el proceso de solicitud ya existe, pero no hay actividad en primer plano;

nombre_paquete y nombre_proceso representan el nombre del paquete de la aplicación y el nombre del proceso;

Proceso: representa información sobre el proceso de la aplicación, incluido el nombre, el identificador de usuario (uid) y el identificador de proceso (pid);

zygote_new_process: indica si se crea un nuevo proceso a través de zygote, aquí es falso;

Activity_hosting_process_count: indica cuántas actividades se alojan en este proceso, aquí es 1;

event_timestamps: indica las marcas de tiempo de varios eventos, por ejemplo, intent_received indica la hora en que se recibe la intención de inicio y first_frame indica la hora en que se muestra el primer fotograma;

to_first_frame: indica el tiempo y los detalles desde que se recibe la intención de inicio hasta que se muestra el primer fotograma, incluido el tiempo total, el tiempo en varios estados del hilo principal, el tiempo de varias operaciones, el uso de varios recursos, etc.;

verificar_clase: indica información sobre la verificación de la carga de clases, incluido el nombre y la hora de la clase;

dlopen_file: indica información sobre cómo abrir archivos de biblioteca compartida, incluidos los nombres de los archivos;

system_state: información que indica el estado del sistema, incluido si dex2oat o installd se están ejecutando, si se envían o reciben transmisiones, qué procesos que no son de inicio son los más activos, etc.;

Práctica perfecta

Activar GC al iniciar

Fenómeno : "Actividad de GC" aparece en slow_start_reason , lo que indica que la actividad de GC ralentiza el inicio de la aplicación durante la fase de inicio.

Análisis : haga clic en [mostrar línea de tiempo] para regresar a la interfaz de la línea de tiempo de Perfetto. En la línea de tiempo de inicio, puede ver que hay un subproceso llamado HeapTaskDaemon , que es el subproceso GC de la aplicación. Estuvo activo durante aproximadamente 100 ms durante la fase de inicio, lo que provocó que la línea de tiempo de actividadResume se alargue en 100 ms. Para evitar que el fenómeno se descubra accidentalmente, realizamos múltiples mediciones y descubrimos que la actividad del GC definitivamente se activará cuando se inicie la aplicación. como muestra la imagen:

Motivo : Según el análisis posterior de la línea de tiempo, se descubrió que la aplicación cargará una fuente especial durante la fase de inicio. La fuente pesa aproximadamente 13 MB. Después de comunicarse con el desarrollo de la aplicación, se confirmó que la fuente se movió a la capa del sistema y la capa de aplicación no necesitan cargar la fuente. Después de eliminar la fuente, GC ya no se activa al 100% cuando se inicia la aplicación.

Operaciones que requieren mucho tiempo en el hilo principal

Fenómeno : "Subproceso principal: tiempo transcurrido en estado de ejecución" aparece en slow_start_reason , lo que significa que durante la fase de inicio, se realizan operaciones que consumen más tiempo en el subproceso principal.

Motivo : Esta situación es muy común durante el desarrollo de aplicaciones. Los desarrolladores de aplicaciones naturalmente colocarán algunas operaciones de adquisición de datos entre procesos bajo el método OnCreate o onStart de la actividad del hilo principal. Aunque estos métodos IPC no activarán ANR, ralentizarán el inicio de la aplicación. aplicación y debe colocarse en el grupo de subprocesos o corrutina para su ejecución.

OpenDexFilesFromOat lleva tiempo

Fenómeno : "Subproceso principal: tiempo invertido en OpenDexFilesFromOat*" aparece en slow_start_reason , lo que indica que se dedica más tiempo a leer archivos dex durante la fase de inicio.

Motivo : esta situación es más común en los sistemas Android de automóviles. Esto puede deberse a que el sistema ha modificado el proceso dex2oat en el sistema para acelerar el inicio, provocando este fenómeno, si no lleva mucho tiempo se puede ignorar.

Solo digo "posible" aquí porque el sistema operativo del automóvil actual tiene muchas modificaciones con respecto a Android nativo para poder comenzar rápidamente. Necesitamos hacer un análisis detallado basado en nuestra propia situación real.

Tiempo de espera de dibujo continuo de varios cuadros

Fenómeno: el tiempo de inicio de una aplicación en Perfetto no es largo, alrededor de 1,3 segundos, pero después de utilizar el método de división de fotogramas, se descubre que la aplicación se retrasará ligeramente después del inicio, lo que provocará que el tiempo de inicio real se extienda a 2,1. s. El rendimiento en Perfetto es el siguiente: después de dibujar el primer fotograma, el tiempo de dibujo de los siguientes 2, 4 y 5 fotogramas supera los 150 ms.

Análisis : la línea de tiempo de dibujo de cuadros proporcionada por Perfetto muestra que la mayor parte del tiempo se dedica al diseño de la vista, lo que muestra que después de dibujar el primer cuadro, se activan múltiples redibujos de página.

Motivo : Al combinar el código con el registro de la aplicación, se descubrió que la aplicación actualiza la página una vez con datos vacíos cuando se inicia y luego obtiene datos de la interfaz IPC para actualizar la página nuevamente. Además, debido a defectos del código, los datos La actualización se ejecutará 4 veces seguidas, lo que provocará esta situación. Modifique el código defectuoso para que ya no se produzca un tiempo de espera de dibujo continuo después del primer fotograma.

Referencias

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

Supongo que te gusta

Origin blog.csdn.net/linkwj/article/details/132460341
Recomendado
Clasificación