O Android 8.0 inclui testes de desempenho de binder e hwbinder para taxa de transferência e latência. Embora existam muitos cenários para detectar problemas de desempenho perceptíveis, a execução de tais cenários pode ser demorada e os resultados geralmente ficam indisponíveis até que um sistema seja integrado. O uso dos testes de desempenho fornecidos facilita o teste durante o desenvolvimento, detecta problemas graves mais cedo e melhora a experiência do usuário.
Os testes de desempenho incluem as quatro categorias a seguir:
- throughput do fichário (disponível em
system/libhwbinder/vts/performance/Benchmark_binder.cpp
) - latência do fichário (disponível em
frameworks/native/libs/binder/tests/schd-dbg.cpp
) - rendimento do hwbinder (disponível em
system/libhwbinder/vts/performance/Benchmark.cpp
) - latência hwbinder (disponível em
system/libhwbinder/vts/performance/Latency.cpp
)
Sobre o fichário e o hwbinder
Binder e hwbinder são infraestruturas de comunicação entre processos (IPC) do Android que compartilham o mesmo driver Linux, mas têm as seguintes diferenças qualitativas:
Aspecto | encadernador | hwbinder |
---|---|---|
Propósito | Forneça um esquema IPC de propósito geral para a estrutura | Comunique-se com o hardware |
Propriedade | Otimizado para uso do framework Android | Baixa latência de sobrecarga mínima |
Alterar a política de agendamento para primeiro plano/segundo plano | Sim | Não |
Argumentos passando | Usa a serialização suportada pelo objeto Parcel | Usa buffers de dispersão e evita a sobrecarga para copiar os dados necessários para a serialização do Parcel |
Herança prioritária | Não | Sim |
Processos Binder e Hwbinder
Um visualizador systrace exibe as transações da seguinte forma:
No exemplo acima:
- Os quatro (4) processos schd-dbg são processos do cliente.
- Os quatro (4) processos de fichário são processos de servidor (o nome começa com Binder e termina com um número de sequência).
- Um processo cliente está sempre emparelhado com um processo servidor, que é dedicado ao seu cliente.
- Todos os pares de processos cliente-servidor são agendados independentemente pelo kernel simultaneamente.
Na CPU 1, o kernel do SO executa o cliente para emitir a solicitação. Em seguida, ele usa a mesma CPU sempre que possível para ativar um processo do servidor, manipular a solicitação e alternar o contexto novamente após a conclusão da solicitação.
Taxa de transferência x latência
Em uma transação perfeita, onde o cliente e o processo do servidor alternam perfeitamente, os testes de taxa de transferência e latência não produzem mensagens substancialmente diferentes. No entanto, quando o kernel do sistema operacional está lidando com uma solicitação de interrupção (IRQ) do hardware, aguardando bloqueios ou simplesmente optando por não lidar com uma mensagem imediatamente, uma bolha de latência pode se formar.
O teste de throughput gera um grande número de transações com diferentes tamanhos de payload, fornecendo uma boa estimativa para o tempo regular da transação (nos melhores cenários) e o throughput máximo que o fichário pode atingir.
Por outro lado, o teste de latência não executa nenhuma ação na carga útil para minimizar o tempo regular da transação. Podemos usar o tempo de transação para estimar a sobrecarga do fichário, fazer estatísticas para o pior caso e calcular a proporção de transações cuja latência atende a um prazo especificado.
Lidar com inversões de prioridade
Uma inversão de prioridade ocorre quando um thread com prioridade mais alta está esperando logicamente por um thread com prioridade mais baixa. Aplicações em tempo real (RT) têm um problema de inversão de prioridade:
Ao usar o agendamento Linux Completely Fair Scheduler (CFS), um encadeamento sempre tem a chance de ser executado mesmo quando outros encadeamentos têm uma prioridade mais alta. Como resultado, os aplicativos com agendamento CFS lidam com a inversão de prioridade como comportamento esperado e não como um problema. Nos casos em que o framework Android precisa de escalonamento RT para garantir o privilégio de threads de alta prioridade, no entanto, a inversão de prioridade deve ser resolvida.
Exemplo de inversão de prioridade durante uma transação de fichário (o encadeamento RT é logicamente bloqueado por outros encadeamentos CFS ao aguardar o serviço de um encadeamento de fichário):
Para evitar bloqueios, você pode usar a herança de prioridade para escalar temporariamente o encadeamento Binder para um encadeamento RT quando ele atender a uma solicitação de um cliente RT. Lembre-se de que o agendamento de RT tem recursos limitados e deve ser usado com cuidado. Em um sistema com n CPUs, o número máximo de threads RT atuais também é n ; encadeamentos RT adicionais podem precisar esperar (e, portanto, perder seus prazos) se todas as CPUs forem tomadas por outros encadeamentos RT.
Para resolver todas as possíveis inversões de prioridade, você pode usar a herança de prioridade para o binder e o hwbinder. No entanto, como o fichário é amplamente usado em todo o sistema, habilitar a herança de prioridade para transações do fichário pode enviar spam ao sistema com mais encadeamentos RT do que ele pode atender.
Executar testes de capacidade
O teste de rendimento é executado em relação ao rendimento da transação do binder/hwbinder. Em um sistema que não está sobrecarregado, as bolhas de latência são raras e seu impacto pode ser eliminado desde que o número de iterações seja alto o suficiente.
- O teste de rendimento do fichário está em
system/libhwbinder/vts/performance/Benchmark_binder.cpp
. - O teste de rendimento do hwbinder está em
system/libhwbinder/vts/performance/Benchmark.cpp
.
Resultado dos testes
Exemplo de resultados de teste de taxa de transferência para transações usando diferentes tamanhos 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
- O tempo indica o atraso de ida e volta medido em tempo real.
- CPU indica o tempo acumulado quando as CPUs são agendadas para o teste.
- Iterações indica o número de vezes que a função de teste foi executada.
Por exemplo, para um payload de 8 bytes:
BM_sendVec_binderize/8 69974 ns 32700 ns 21296
… o rendimento máximo que o fichário pode atingir é calculado como:
Taxa de transferência MAX com carga útil de 8 bytes = (8 * 21296)/69974 ~= 2,423 b/ns ~= 2,268 Gb/s
opções de teste
Para obter resultados em .json, execute o teste com o 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"
},
….
}
Executar testes de latência
O teste de latência mede o tempo que leva para o cliente iniciar a inicialização da transação, alternar para o processo do servidor para manipulação e receber o resultado. O teste também procura por comportamentos ruins conhecidos do agendador que podem afetar negativamente a latência da transação, como um agendador que não oferece suporte à herança de prioridade ou honra o sinalizador de sincronização.
- O teste de latência do fichário está em
frameworks/native/libs/binder/tests/schd-dbg.cpp
. - O teste de latência hwbinder está em
system/libhwbinder/vts/performance/Latency.cpp
.
Resultado dos testes
Os resultados (em .json) mostram estatísticas de latência média/melhor/pior e o número de prazos perdidos.
opções de teste
Os testes de latência aceitam as seguintes opções:
Comando | Descrição |
---|---|
-i value | Especifique o número de iterações. |
-pair value | Especifique o número de pares de processos. |
-deadline_us 2500 | Especifique o prazo em nós. |
-v | Obter saída detalhada (depuração). |
-trace | Interrompa o rastreamento em um prazo atingido. |
As seções a seguir detalham cada opção, descrevem o uso e fornecem exemplos de resultados.
Especificar iterações
Exemplo com um grande número de iterações e saída detalhada desativada:
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"
}
Esses resultados de teste mostram o seguinte:
-
"pair":3
- Cria um par de cliente e servidor.
-
"iterations": 5000
- Inclui 5000 iterações.
-
"deadline_us":2500
- Prazo é 2500us (2,5ms); espera-se que a maioria das transações atinja esse valor.
-
"I": 10000
- Uma única iteração de teste inclui duas (2) transações:
- Uma transação por prioridade normal (
CFS other
) - Uma transação por prioridade em tempo real (
RT-fifo
)
- Uma transação por prioridade normal (
-
"S": 9352
- 9352 das transações são sincronizadas na mesma CPU.
-
"R": 0.9352
- Indica a proporção na qual o cliente e o servidor são sincronizados juntos na mesma CPU.
-
"other_ms":{ "avg":0.2 , "wst":2.8 , "bst":0.053, "miss":2, "meetR":0.9996}
- O caso médio (
avg
), pior (wst
) e melhor (bst
) para todas as transações emitidas por um chamador de prioridade normal. Duas transaçõesmiss
o prazo, fazendo com que o índice de atendimento (meetR
) seja 0,9996. -
"fifo_ms": { "avg":0.16, "wst":1.5 , "bst":0.067, "miss":0, "meetR":1}
- Semelhante a
other_ms
, mas para transações emitidas pelo cliente com prioridadert_fifo
. É provável (mas não obrigatório) que ofifo_ms
tenha um resultado melhor queother_ms
, com valoresavg
ewst
mais baixos e ummeetR
mais alto (a diferença pode ser ainda mais significativa com carga em segundo plano).
Observação: o carregamento em segundo plano pode afetar o resultado da taxa de transferência e a tupla other_ms
no teste de latência. Somente o fifo_ms
pode mostrar resultados semelhantes, desde que o carregamento em segundo plano tenha uma prioridade mais baixa que RT-fifo
.
Especificar valores de par
Cada processo do cliente é emparelhado com um processo do servidor dedicado ao cliente e cada par pode ser agendado independentemente para qualquer CPU. No entanto, a migração da CPU não deve ocorrer durante uma transação, desde que o sinalizador SYNC seja honor
.
Certifique-se de que o sistema não está sobrecarregado! Embora seja esperada alta latência em um sistema sobrecarregado, os resultados do teste para um sistema sobrecarregado não fornecem informações úteis. Para testar um sistema com pressão mais alta, use -pair #cpu-1
(ou -pair #cpu
com cuidado). Testar usando -pair n
com n > #cpu
sobrecarrega o sistema e gera informações inúteis.
Especificar valores de prazo
Após extensos testes de cenário do usuário (executando o teste de latência em um produto qualificado), determinamos que 2,5 ms é o prazo a ser cumprido. Para novos aplicativos com requisitos maiores (como 1000 fotos/segundo), esse valor de prazo será alterado.
Especificar saída detalhada
O uso da opção -v
exibe a saída detalhada. Exemplo:
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
- O thread de serviço é criado com uma prioridade
SCHED_OTHER
e executado naCPU:1
compid 8674
. - A primeira transação é então iniciada por um
fifo-caller
. Para atender a essa transação, o hwbinder atualiza a prioridade do servidor (pid: 8674 tid: 8676
) para 99 e também a marca com uma classe de agendamento transitória (impressa como???
). O escalonador então coloca o processo do servidor emCPU:0
para rodar e sincroniza-o com a mesma CPU com seu cliente. - O chamador da segunda transação tem uma prioridade
SCHED_OTHER
. O servidor se rebaixa e atende o chamador com prioridadeSCHED_OTHER
.
Usar rastreamento para depuração
Você pode especificar a opção -trace
para depurar problemas de latência. Quando usado, o teste de latência interrompe a gravação do tracelog no momento em que uma latência ruim é detectada. Exemplo:
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
Os seguintes componentes podem afetar a latência:
- Modo de construção do Android . O modo Eng geralmente é mais lento que o modo userdebug.
- Quadro . Como o serviço de estrutura usa
ioctl
para configurar o fichário? - Driver de fichário . O driver oferece suporte ao bloqueio refinado? Ele contém todos os patches de conversão de desempenho?
- Versão do núcleo . Quanto melhor a capacidade de tempo real do kernel, melhores os resultados.
- Configuração do kernel . A configuração do kernel contém configurações
DEBUG
, comoDEBUG_PREEMPT
eDEBUG_SPIN_LOCK
? - Agendador de kernel . O kernel tem um agendador Energy-Aware (EAS) ou um agendador Heterogeneous Multi-Processing (HMP)? Algum driver do kernel (driver
cpu-freq
, drivercpu-idle
,cpu-hotplug
, etc.) afeta o agendador?