Google is committed to advancing racial equity for Black communities. See how.
Se usó la API de Cloud Translation para traducir esta página.
Switch to English

Pruebas de rendimiento

Android 8.0 incluye pruebas de rendimiento de binder y hwbinder para rendimiento y latencia. Si bien existen muchos escenarios para detectar problemas de rendimiento perceptibles, ejecutar dichos escenarios puede llevar mucho tiempo y los resultados a menudo no están disponibles hasta que se integra un sistema. El uso de las pruebas de rendimiento proporcionadas facilita la realización de pruebas durante el desarrollo, detecta problemas serios antes y mejora la experiencia del usuario.

Las pruebas de rendimiento incluyen las siguientes cuatro categorías:

  • rendimiento de la carpeta (disponible en system/libhwbinder/vts/performance/Benchmark_binder.cpp )
  • latencia de binder (disponible en frameworks/native/libs/binder/tests/schd-dbg.cpp )
  • Rendimiento de hwbinder (disponible en system/libhwbinder/vts/performance/Benchmark.cpp )
  • latencia de hwbinder (disponible en system/libhwbinder/vts/performance/Latency.cpp )

Acerca de Binder y Hwbinder

Binder y hwbinder son infraestructuras de comunicación entre procesos (IPC) de Android que comparten el mismo controlador de Linux, pero tienen las siguientes diferencias cualitativas:

Aspecto aglutinante hwbinder
Propósito Proporcionar un esquema de IPC de propósito general para el marco Comunicarse con el hardware
Propiedad Optimizado para el uso del marco de trabajo de Android Latencia mínima de sobrecarga baja
Cambiar la política de programación para primer plano / segundo plano si No
Pasando argumentos Utiliza la serialización compatible con el objeto Parcel Utiliza búferes de dispersión y evita la sobrecarga de copiar los datos necesarios para la serialización de paquetes.
Herencia prioritaria No si

Procesos aglutinantes y aglutinantes

Un visualizador de systrace muestra las transacciones de la siguiente manera:

Figura 1. Visualización de Systrace de procesos de enlace.

En el ejemplo anterior:

  • Los cuatro (4) procesos schd-dbg son procesos cliente.
  • Los cuatro (4) procesos de Binder son procesos de servidor (el nombre comienza con Binder y termina con un número de secuencia).
  • Un proceso de cliente siempre está emparejado con un proceso de servidor, que está dedicado a su cliente.
  • Todos los pares de procesos cliente-servidor se programan de forma independiente por kernel al mismo tiempo.

En la CPU 1, el kernel del sistema operativo ejecuta al cliente para emitir la solicitud. Luego usa la misma CPU siempre que sea posible para reactivar un proceso de servidor, manejar la solicitud y volver a cambiar de contexto después de que se complete la solicitud.

Rendimiento frente a latencia

En una transacción perfecta, donde el proceso del cliente y el servidor cambian sin problemas, las pruebas de rendimiento y latencia no producen mensajes sustancialmente diferentes. Sin embargo, cuando el kernel del sistema operativo está manejando una solicitud de interrupción (IRQ) del hardware, esperando bloqueos o simplemente eligiendo no manejar un mensaje inmediatamente, se puede formar una burbuja de latencia.

Figura 2. Burbuja de latencia debido a diferencias en el rendimiento y la latencia.

La prueba de rendimiento genera una gran cantidad de transacciones con diferentes tamaños de carga útil, lo que proporciona una buena estimación del tiempo de transacción regular (en el mejor de los casos) y el rendimiento máximo que puede lograr el vinculador.

Por el contrario, la prueba de latencia no realiza acciones en la carga útil para minimizar el tiempo de transacción habitual. Podemos usar el tiempo de transacción para estimar la sobrecarga de la carpeta, hacer estadísticas para el peor de los casos y calcular la proporción de transacciones cuya latencia cumple con un plazo específico.

Manejo de inversiones prioritarias

Una inversión de prioridad ocurre cuando un hilo con mayor prioridad está lógicamente esperando a un hilo con menor prioridad. Las aplicaciones en tiempo real (RT) tienen un problema de inversión de prioridad:

Figura 3. Inversión de prioridades en aplicaciones en tiempo real.

Cuando se utiliza la programación de Linux Completely Fair Scheduler (CFS), un hilo siempre tiene la posibilidad de ejecutarse incluso cuando otros hilos tienen una prioridad más alta. Como resultado, las aplicaciones con programación CFS manejan la inversión de prioridad como un comportamiento esperado y no como un problema. Sin embargo, en los casos en que el marco de Android necesita programación RT para garantizar el privilegio de subprocesos de alta prioridad, se debe resolver la inversión de prioridad.

Ejemplo de inversión de prioridad durante una transacción de enlace (el subproceso de RT está bloqueado lógicamente por otros subprocesos de CFS cuando se espera que entre en servicio un subproceso de enlace):

Figura 4. Inversión de prioridad, hilos bloqueados en tiempo real.

Para evitar bloqueos, puede usar la herencia de prioridad para escalar temporalmente el hilo de Binder a un hilo de RT cuando atiende una solicitud de un cliente de RT. Tenga en cuenta que la programación de RT tiene recursos limitados y debe usarse con cuidado. En un sistema con n CPU, el número máximo de subprocesos RT actuales también es n ; Es posible que los subprocesos RT adicionales deban esperar (y, por lo tanto, no cumplan con sus fechas límite) si otros subprocesos RT toman todas las CPU.

Para resolver todas las posibles inversiones de prioridad, puede utilizar la herencia de prioridad para binder y hwbinder. Sin embargo, como Binder se usa ampliamente en todo el sistema, habilitar la herencia de prioridad para las transacciones de Binder podría enviar spam al sistema con más subprocesos RT de los que puede atender.

Ejecución de pruebas de rendimiento

La prueba de rendimiento se ejecuta contra el rendimiento de la transacción de binder / hwbinder. En un sistema que no está sobrecargado, las burbujas de latencia son raras y su impacto puede eliminarse siempre que el número de iteraciones sea lo suficientemente alto.

  • La prueba de rendimiento de la carpeta se encuentra en system/libhwbinder/vts/performance/Benchmark_binder.cpp .
  • La prueba de rendimiento de hwbinder se encuentra en system/libhwbinder/vts/performance/Benchmark.cpp .

Resultados de la prueba

Ejemplo de resultados de pruebas de rendimiento para transacciones que utilizan diferentes tamaños de carga útil:

Benchmark                      Time          CPU           Iterations
---------------------------------------------------------------------
BM_sendVec_binderize/4         70302 ns      32820 ns      21054
BM_sendVec_binderize/8         69974 ns      32700 ns      21296
BM_sendVec_binderize/16        70079 ns      32750 ns      21365
BM_sendVec_binderize/32        69907 ns      32686 ns      21310
BM_sendVec_binderize/64        70338 ns      32810 ns      21398
BM_sendVec_binderize/128       70012 ns      32768 ns      21377
BM_sendVec_binderize/256       69836 ns      32740 ns      21329
BM_sendVec_binderize/512       69986 ns      32830 ns      21296
BM_sendVec_binderize/1024      69714 ns      32757 ns      21319
BM_sendVec_binderize/2k        75002 ns      34520 ns      20305
BM_sendVec_binderize/4k        81955 ns      39116 ns      17895
BM_sendVec_binderize/8k        95316 ns      45710 ns      15350
BM_sendVec_binderize/16k      112751 ns      54417 ns      12679
BM_sendVec_binderize/32k      146642 ns      71339 ns       9901
BM_sendVec_binderize/64k      214796 ns     104665 ns       6495
  • El tiempo indica el retraso de ida y vuelta medido en tiempo real.
  • CPU indica el tiempo acumulado cuando las CPU están programadas para la prueba.
  • Iteraciones indica el número de veces que se ejecutó la función de prueba.

Por ejemplo, para una carga útil de 8 bytes:

BM_sendVec_binderize/8         69974 ns      32700 ns      21296

... el rendimiento máximo que puede alcanzar el aglutinante se calcula como:

Rendimiento máximo con carga útil de 8 bytes = (8 * 21296) / 69974 ~ = 2.423 b / ns ~ = 2.268 Gb / s

Opciones de prueba

Para obtener resultados en .json, ejecute la prueba con el argumento --benchmark_format=json :

libhwbinder_benchmark --benchmark_format=json
{
  "context": {
    "date": "2017-05-17 08:32:47",
    "num_cpus": 4,
    "mhz_per_cpu": 19,
    "cpu_scaling_enabled": true,
    "library_build_type": "release"
  },
  "benchmarks": [
    {
      "name": "BM_sendVec_binderize/4",
      "iterations": 32342,
      "real_time": 47809,
      "cpu_time": 21906,
      "time_unit": "ns"
    },
   ….
}

Ejecución de pruebas de latencia

La prueba de latencia mide el tiempo que tarda el cliente en comenzar a inicializar la transacción, cambiar al proceso del servidor para su manejo y recibir el resultado. La prueba también busca comportamientos incorrectos conocidos del programador que pueden afectar negativamente la latencia de la transacción, como un programador que no admite la herencia de prioridad o respeta la marca de sincronización.

  • La prueba de latencia de frameworks/native/libs/binder/tests/schd-dbg.cpp encuentra en frameworks/native/libs/binder/tests/schd-dbg.cpp .
  • La prueba de latencia de hwbinder se encuentra en system/libhwbinder/vts/performance/Latency.cpp .

Resultados de la prueba

Los resultados (en .json) muestran estadísticas de latencia promedio / mejor / peor y el número de fechas límite incumplidas.

Opciones de prueba

Las pruebas de latencia tienen las siguientes opciones:

Mando Descripción
-i value Especifique el número de iteraciones.
-pair value Especifique el número de pares de procesos.
-deadline_us 2500 Especifique el plazo en nosotros.
-v Obtenga una salida detallada (depuración).
-trace Detén el rastro en una fecha límite.

Las siguientes secciones detallan cada opción, describen el uso y proporcionan resultados de ejemplo.

Especificando iteraciones

Ejemplo con una gran cantidad de iteraciones y salida detallada deshabilitada:

libhwbinder_latency -i 5000 -pair 3
{
"cfg":{"pair":3,"iterations":5000,"deadline_us":2500},
"P0":{"SYNC":"GOOD","S":9352,"I":10000,"R":0.9352,
  "other_ms":{ "avg":0.2 , "wst":2.8 , "bst":0.053, "miss":2, "meetR":0.9996},
  "fifo_ms": { "avg":0.16, "wst":1.5 , "bst":0.067, "miss":0, "meetR":1}
},
"P1":{"SYNC":"GOOD","S":9334,"I":10000,"R":0.9334,
  "other_ms":{ "avg":0.19, "wst":2.9 , "bst":0.055, "miss":2, "meetR":0.9996},
  "fifo_ms": { "avg":0.16, "wst":3.1 , "bst":0.066, "miss":1, "meetR":0.9998}
},
"P2":{"SYNC":"GOOD","S":9369,"I":10000,"R":0.9369,
  "other_ms":{ "avg":0.19, "wst":4.8 , "bst":0.055, "miss":6, "meetR":0.9988},
  "fifo_ms": { "avg":0.15, "wst":1.8 , "bst":0.067, "miss":0, "meetR":1}
},
"inheritance": "PASS"
}

Los resultados de estas pruebas muestran lo siguiente:

"pair":3
Crea un par de cliente y servidor.
"iterations": 5000
Incluye 5000 iteraciones.
"deadline_us":2500
La fecha límite es 2500us (2.5ms); Se espera que la mayoría de las transacciones alcancen este valor.
"I": 10000
Una sola iteración de prueba incluye dos (2) transacciones:
  • Una transacción por prioridad normal ( CFS other )
  • Una transacción por prioridad en tiempo real ( RT-fifo )
5000 iteraciones equivalen a un total de 10000 transacciones.
"S": 9352
9352 de las transacciones se sincronizan en la misma CPU.
"R": 0.9352
Indica la proporción en la que el cliente y el servidor se sincronizan juntos en la misma CPU.
"other_ms":{ "avg":0.2 , "wst":2.8 , "bst":0.053, "miss":2, "meetR":0.9996}
El caso promedio ( avg ), peor ( wst ) y mejor ( bst ) para todas las transacciones emitidas por un llamador prioritario normal. Dos transacciones miss cumplen con la fecha límite, lo que hace que el índice de cumplimiento ( meetR ) sea de 0.9996.
"fifo_ms": { "avg":0.16, "wst":1.5 , "bst":0.067, "miss":0, "meetR":1}
Similar a other_ms , pero para transacciones emitidas por cliente con prioridad rt_fifo . Es probable (pero no obligatorio) que fifo_ms tenga un mejor resultado que other_ms , con valores de avg y wst bajos y un meetR más meetR (la diferencia puede ser aún más significativa con la carga en segundo plano).

Nota: La carga en segundo plano puede afectar el resultado de rendimiento y la tupla other_ms en la prueba de latencia. Solo el fifo_ms puede mostrar resultados similares siempre que la carga de fondo tenga una prioridad más baja que la RT-fifo .

Especificar valores de pares

Cada proceso de cliente está emparejado con un proceso de servidor dedicado para el cliente, y cada par se puede programar de forma independiente a cualquier CPU. Sin embargo, la migración de la CPU no debería ocurrir durante una transacción siempre que el indicador SYNC sea de honor .

¡Asegúrese de que el sistema no esté sobrecargado! Si bien se espera una alta latencia en un sistema sobrecargado, los resultados de las pruebas para un sistema sobrecargado no proporcionan información útil. Para probar un sistema con una presión más alta, use -pair #cpu-1 (o -pair #cpu con precaución). La prueba usando -pair n con n > #cpu sobrecarga el sistema y genera información inútil.

Especificar valores de fecha límite

Después de pruebas exhaustivas de escenarios de usuario (ejecutando la prueba de latencia en un producto calificado), determinamos que 2.5ms es el plazo para cumplir. Para aplicaciones nuevas con requisitos más altos (como 1000 fotos / segundo), este valor de fecha límite cambiará.

Especificar salida detallada

El uso de la opción -v muestra una salida detallada. Ejemplo:

libhwbinder_latency -i 1 -v

-------------------------------------------------- service pid: 8674 tid: 8674 cpu: 1 SCHED_OTHER 0
-------------------------------------------------- main pid: 8673 tid: 8673 cpu: 1 -------------------------------------------------- client pid: 8677 tid: 8677 cpu: 0 SCHED_OTHER 0
-------------------------------------------------- fifo-caller pid: 8677 tid: 8678 cpu: 0 SCHED_FIFO 99 -------------------------------------------------- hwbinder pid: 8674 tid: 8676 cpu: 0 ??? 99
-------------------------------------------------- other-caller pid: 8677 tid: 8677 cpu: 0 SCHED_OTHER 0 -------------------------------------------------- hwbinder pid: 8674 tid: 8676 cpu: 0 SCHED_OTHER 0
  • El subproceso de servicio se crea con una prioridad SCHED_OTHER y se ejecuta en CPU:1 con pid 8674 .
  • La primera transacción la inicia entonces un fifo-caller . Para atender esta transacción, el hwbinder actualiza la prioridad del servidor ( pid: 8674 tid: 8676 ) a 99 y también lo marca con una clase de programación transitoria (impresa como ??? ). Luego, el programador coloca el proceso del servidor en CPU:0 para que se ejecute y lo sincroniza con la misma CPU con su cliente.
  • La segunda persona que llama a la transacción tiene prioridad SCHED_OTHER . El servidor se degrada a sí mismo y SCHED_OTHER la persona que llama con prioridad SCHED_OTHER .

Usar rastreo para depurar

Puede especificar la opción -trace para depurar problemas de latencia. Cuando se usa, la prueba de latencia detiene la grabación del registro de seguimiento en el momento en que se detecta una latencia incorrecta. Ejemplo:

atrace --async_start -b 8000 -c sched idle workq binder_driver sync freq
libhwbinder_latency -deadline_us 50000 -trace -i 50000 -pair 3
deadline triggered: halt ∓ stop trace
log:/sys/kernel/debug/tracing/trace

Los siguientes componentes pueden afectar la latencia:

  • Modo de compilación de Android . El modo Eng suele ser más lento que el modo de depuración del usuario.
  • Marco . ¿Cómo usa el servicio de marco ioctl para configurar el enlazador?
  • Conductor de carpeta . ¿El controlador admite el bloqueo de grano fino? ¿Contiene todos los parches de cambio de rendimiento?
  • Versión de kernel . Cuanto mejor sea la capacidad en tiempo real del kernel, mejores serán los resultados.
  • Configuración del kernel ¿La configuración del kernel contiene configuraciones DEBUG como DEBUG_PREEMPT y DEBUG_SPIN_LOCK ?
  • Programador de kernel . ¿Tiene el kernel un programador Energy-Aware (EAS) o un programador heterogéneo de multiprocesamiento (HMP)? ¿Algún controlador del kernel (controlador cpu-freq , controlador cpu-idle , cpu-hotplug , etc.) afecta al programador?