Pruebas de rendimiento

Android 8.0 incluye pruebas de rendimiento de Binder y hwbinder para la capacidad de procesamiento y latencia. Si bien existen muchas situaciones para detectar el rendimiento perceptible problemas, ejecutar esas situaciones puede llevar mucho tiempo y los resultados suelen ser disponible hasta que se integra un sistema. Cómo usar el rendimiento proporcionado pruebas facilitan la realización de pruebas durante el desarrollo y la detección de problemas graves y mejorar la experiencia del usuario.

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

  • capacidad de procesamiento de Binder (disponible en system/libhwbinder/vts/performance/Benchmark_binder.cpp)
  • latencia de Binder (disponible en frameworks/native/libs/binder/tests/schd-dbg.cpp)
  • capacidad de procesamiento de hwbinder (disponible en system/libhwbinder/vts/performance/Benchmark.cpp)
  • Latencia de hwbinder (disponible en system/libhwbinder/vts/performance/Latency.cpp)

Información acerca de Binder y hwbinder

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

Aspecto carpeta Hwbinder
Propósito Proporcionar un esquema IPC de uso general para el framework Comunicación con el hardware
Propiedad Optimizado para el uso del framework de Android Latencia baja de sobrecarga mínima
Cambia la política de programación para el uso en primer o segundo plano No
Argumentos que pasan Utiliza la serialización compatible con el objeto Parcel Usa búferes de dispersión y evita la sobrecarga para copiar los datos necesarios para Serialización de paquetes
Herencia de prioridad No

Procesos de Binder y hwbinder

Un visualizador de Systrace muestra transacciones de la siguiente manera:

Figura 1: Visualización de Systrace de Binder procesos.

En el ejemplo anterior:

  • Los cuatro (4) procesos schd-dbg son procesos de 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 del cliente siempre está vinculado con un proceso de servidor, que es a su cliente.
  • El kernel programa de forma independiente todos los pares de procesos cliente-servidor de forma simultánea.

En la CPU 1, el kernel del SO ejecuta el cliente para emitir la solicitud. Luego, usa la misma CPU siempre que sea posible para activar un proceso del servidor, controlar solicitud, y el cambio de contexto una vez que se completa la solicitud.

Capacidad de procesamiento frente a latencia

En una transacción perfecta, donde el proceso del cliente y del servidor cambian. sin problemas, las pruebas de capacidad de procesamiento y latencia no producen mensajes nuevos. Sin embargo, cuando el kernel del SO maneja una solicitud de interrupción (IRQ) del hardware, esperar bloqueos o simplemente no controlar un mensaje de inmediato, se puede formar una burbuja de latencia.

Figura 2: Cuadro de latencia debido a diferencias en la capacidad de procesamiento y la latencia.

La prueba de capacidad de procesamiento genera una gran cantidad de transacciones con diferentes de cargas útiles, lo que proporciona una buena estimación del tiempo de transacción regular (en en el mejor de los casos) y la capacidad de procesamiento máxima que Binder puede alcanzar.

Por el contrario, la prueba de latencia no realiza acciones en la carga útil para minimizar la hora de la transacción regular. Podemos usar el tiempo de transacción para estimar sobrecargar, generar estadísticas para el peor de los casos y calcular la proporción transacciones cuya latencia cumple con un plazo especificado.

Cómo controlar las inversiones de prioridad

Una inversión de prioridad se produce cuando, de forma lógica, un subproceso con mayor prioridad esperando un subproceso con menor prioridad. Las aplicaciones en tiempo real (RT) tienen un Problema de inversión de prioridad:

Figura 3: Inversión de prioridad en tiempo real aplicaciones.

Cuando se usa la programación Completaly Fair Scheduler (CFS) de Linux, un subproceso siempre puede ejecutarse incluso cuando otros subprocesos tienen una prioridad más alta. Como resultado, las aplicaciones con programación CFS manejan la inversión de prioridad como el comportamiento esperado y no como un problema. Cuando el framework de Android necesite programación de RT para garantizar el privilegio de los subprocesos de alta prioridad; sin embargo, la inversión de prioridad debe resolverse.

Ejemplo de inversión de prioridad durante una transacción de Binder (el subproceso RT es lógicamente bloqueados por otros subprocesos de CFS cuando se espera que un subproceso de Binder servicio):

Figura 4: Inversión de prioridad, bloqueada en tiempo real conversaciones.

Para evitar bloqueos, puedes usar la herencia de prioridad para escalar temporalmente el subproceso de Binder a un subproceso de RT cuando envía una solicitud de un cliente de RT. Ten en cuenta que la programación de RT tiene recursos limitados y se debe usar con atención. En un sistema con n CPU, la cantidad máxima de RT actual subprocesos también es n; es posible que los subprocesos de RT adicionales deban esperar (y, por lo tanto, no cumplen con sus plazos) si otros subprocesos de RT toman todas las CPU.

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

Ejecuta pruebas de capacidad de procesamiento

La prueba de capacidad de procesamiento se ejecuta con la capacidad de procesamiento de transacciones de Binder/hwbinder. En en un sistema que no está sobrecargado, las burbujas de latencia son escasas y su impacto pueden eliminarse, siempre y cuando el número de iteraciones sea lo suficientemente alto.

  • La prueba de capacidad de procesamiento de binder se encuentra en system/libhwbinder/vts/performance/Benchmark_binder.cpp
  • La prueba de capacidad de procesamiento de hwbinder se encuentra en system/libhwbinder/vts/performance/Benchmark.cpp

Resultados de la prueba

Ejemplo de resultados de la prueba de capacidad de procesamiento para transacciones que usan una carga útil diferente tamaños:

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
  • Time indica el retraso de ida y vuelta medido en tiempo real.
  • CPU indica el tiempo acumulado en el que se programan las CPU. para la prueba.
  • Las iteraciones indican la cantidad de veces que la función de prueba. ejecutado.

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

BM_sendVec_binderize/8         69974 ns      32700 ns      21296

La capacidad de procesamiento máxima que puede alcanzar Binder se calcula de la siguiente manera:

Capacidad de procesamiento de MAX 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, ejecuta la prueba con el archivo 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"
    },
   ….
}

Ejecuta pruebas de latencia

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

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

Resultados de la prueba

Los resultados (en .json) muestran las estadísticas de latencia promedio/mejor/peor y la cantidad de fechas límite incumplidas.

Opciones de prueba

Las pruebas de latencia toman las siguientes opciones:

Comando Descripción
-i value Especifica la cantidad de iteraciones.
-pair value Especifica la cantidad de pares de procesos.
-deadline_us 2500 Especifica la fecha límite.
-v Obtiene un resultado detallado (depuración).
-trace Detén el registro cuando se cumpla un plazo.

En las siguientes secciones, se detalla cada opción, se describe el uso y se proporciona resultados de ejemplo.

Cómo especificar iteraciones

Ejemplo con una gran cantidad de iteraciones y resultados detallados inhabilitados:

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"
}

Estos resultados de la prueba muestran lo siguiente:

"pair":3
Crea un par de cliente y servidor.
"iterations": 5000
Incluye 5,000 iteraciones.
"deadline_us":2500
El plazo es de 2,500 us (2.5 ms). se espera que la mayoría de las transacciones cumplan 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)
5,000 iteraciones equivale a un total de 10,000 transacciones.
"S": 9352
9,352 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 en la misma CPU.
"other_ms":{ "avg":0.2 , "wst":2.8 , "bst":0.053, "miss":2, "meetR":0.9996}
El promedio (avg), el peor (wst) y el mejor (bst) para todas las transacciones emitidas por un emisor con prioridad normal. Dos transacciones miss la fecha límite, lo que hace que la proporción de cumplimiento (meetR) 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 el cliente con Prioridad rt_fifo. Es probable (pero no obligatorio) que el fifo_ms tiene un mejor resultado que other_ms, con menor Valores avg y wst, y un meetR más alto (la diferencia puede ser aún más significativa con la carga en segundo plano).

Nota: La carga en segundo plano puede afectar la capacidad de procesamiento y la tupla other_ms en la prueba de latencia. Solo los Es posible que fifo_ms muestre resultados similares, siempre y cuando la carga en segundo plano haya una prioridad menor que RT-fifo.

Especifica valores de par

Cada proceso del cliente se vincula con un proceso de servidor dedicado al cliente y cada par puede programarse de forma independiente para cualquier CPU. Sin embargo, la CPU la migración no debe ocurrir durante una transacción siempre que la marca SYNC se honor

Asegúrate de que el sistema no esté sobrecargado. Si bien la alta latencia en entornos con sobrecarga el sistema operativo, los resultados de las pruebas para un sistema sobrecargado no proporcionan información. Para probar un sistema con una presión más alta, usa -pair #cpu-1 (o -pair #cpu con cuidado). Pruebas con -pair n con n > #cpu sobrecarga la y genera información inútil.

Especifica los valores de plazo

Después de realizar pruebas exhaustivas de situaciones de usuario (ejecutar la prueba de latencia en un calificado), determinamos que el plazo para cumplir es de 2,5 ms. Para nuevos aplicaciones con requisitos más elevados (como 1,000 fotos por segundo), este el valor del plazo cambiará.

Especificar un resultado detallado

Si usas la opción -v, se muestra un resultado detallado. 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 un SCHED_OTHER y se ejecuta en CPU:1 con pid 8674.
  • Luego, se inicia la primera transacción con un fifo-caller Para realizar esta transacción, hwbinder actualiza la del servidor (pid: 8674 tid: 8676) en 99 y también la marca con una clase de programación transitoria (impresa como ???) El programador Luego, coloca el proceso del servidor en CPU:0 para que se ejecute y lo sincroniza con el la misma CPU con su cliente.
  • El emisor de la segunda transacción tiene un Prioridad SCHED_OTHER. El servidor cambia a una versión inferior y entrega llamador con prioridad SCHED_OTHER.

Cómo usar Trace para la depuración

Puedes especificar la opción -trace para depurar los problemas de latencia. Cuándo la latencia, la prueba de latencia detiene el registro de tracelog en el momento en que la si se detecta latencia. 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 de ingeniería suele ser más lento que el modo userdebug.
  • Marco de trabajo. ¿Cómo usa el servicio de framework ¿ioctl para configurar Binder?
  • Controlador de Binder ¿El controlador admite reglas de el bloqueo? ¿Contiene todos los parches de transformación de rendimiento?
  • Versión de kernel. A mayor capacidad en tiempo real, el kernel mejores serán los resultados.
  • Configuración del kernel. ¿La configuración del kernel contiene Los parámetros de configuración de DEBUG, como DEBUG_PREEMPT y ¿DEBUG_SPIN_LOCK?
  • Programador de kernel. ¿El kernel tiene una configuración de eficiencia energética? de procesamiento multiprocesamiento heterogéneo (EAS) o HMP? Usa cualquier kernel conductores (cpu-freq conductor, cpu-idle conductor, cpu-hotplug, etc.) afectan al programador?