Use o Simpleperf para avaliar o desempenho de um dispositivo. O Simpleperf é uma ferramenta de criação de perfil nativa para apps e processos nativos no Android. Use o CPU Profiler para inspecionar o uso da CPU e a atividade da linha de execução do app em tempo real.
Há dois indicadores de desempenho visíveis para o usuário:
- Desempenho previsível e perceptível. A interface do usuário (UI) perde frames ou renderiza consistentemente a 60 QPS? O áudio é reproduzido sem artefatos ou estalos? Qual é o tempo de atraso entre o toque do usuário na tela e a exibição do efeito?
- Tempo necessário para operações mais longas (como abrir apps).
O primeiro é mais perceptível do que o segundo. Os usuários geralmente notam o jank, mas não conseguem diferenciar 500 ms de 600 ms no tempo de inicialização do app, a menos que esteja olhando dois dispositivos lado a lado. A latência do toque é imediatamente perceptível e contribui significativamente para a percepção de um dispositivo.
Como resultado, em um dispositivo rápido, o pipeline de interface é a coisa mais importante no sistema, além do que é necessário para manter o pipeline de interface funcional. Isso significa que o pipeline da interface precisa impedir qualquer outro trabalho que não seja necessário para a interface fluida. Para manter uma interface fluida, a sincronização em segundo plano, o envio de notificações e trabalhos semelhantes precisam ser adiados se o trabalho da interface puder ser executado. É aceitável trocar o desempenho de operações mais longas (tempo de execução HDR+, inicialização de apps etc.) para manter uma interface fluida.
Capacidade versus jitter
Ao considerar o desempenho do dispositivo, capacidade e jitter são duas métricas significativas.
Capacity
A capacidade é a quantidade total de algum recurso que o dispositivo possui ao longo de um período. Isso pode ser recursos de CPU, GPU, E/S, rede, largura de banda de memória ou qualquer métrica semelhante. Ao examinar o desempenho do sistema inteiro, pode ser útil abstrair os componentes individuais e assumir uma única métrica que determina o desempenho, especialmente ao ajustar um novo dispositivo, porque as cargas de trabalho executadas nele provavelmente são fixas.
A capacidade de um sistema varia de acordo com os recursos de computação on-line. Alterar a frequência da CPU/GPU é a principal forma de mudar a capacidade, mas há outras, como mudar o número de núcleos de CPU on-line. Assim, a capacidade de um sistema corresponde ao consumo de energia. Alterar a capacidade sempre resulta em uma mudança semelhante no consumo de energia.
A capacidade necessária em um determinado momento é determinada principalmente pelo app em execução. Como resultado, a plataforma não pode fazer muito para ajustar a capacidade necessária para uma determinada carga de trabalho, e os meios para fazer isso são limitados a melhorias de execução (framework do Android, ART, Bionic, compilador/drivers de GPU, kernel).
Instabilidade
Embora a capacidade necessária para uma carga de trabalho seja fácil de identificar, o jitter é um conceito mais vago. Para uma boa introdução ao jitter como um impedimento para sistemas rápidos, recomendamos a leitura do artigo intitulado The Case of the Missing Supercomputer Performance: Achieving Optimal Performance on the 8,192 processors of ASCI Q. É uma investigação sobre por que o supercomputador ASCI Q não alcançou o desempenho esperado e é uma ótima introdução à otimização de sistemas grandes.
Esta página usa o termo jitter para descrever o que o artigo da ASCI Q chama de ruído. O jitter é o comportamento aleatório do sistema que impede a execução de trabalhos perceptíveis. Muitas vezes, é um trabalho que precisa ser executado, mas pode não ter requisitos de tempo rígidos que o façam ser executado em um determinado momento. Como é aleatório, é extremamente difícil refutar a existência de jitter para uma determinada carga de trabalho. Também é extremamente difícil provar que uma fonte conhecida de jitter foi a causa de um problema de desempenho específico. As ferramentas mais usadas para diagnosticar as causas de jitter (como rastreamento ou registro) podem introduzir o próprio jitter.
As fontes de jitter encontradas em implementações reais do Android incluem:
- Atraso do programador
- Gerenciadores de interrupção
- O código do driver fica em execução por muito tempo com a preempção ou as interrupções desativadas.
- Softirqs de longa duração
- Contenção de bloqueio (app, framework, driver do kernel, bloqueio de vinculação, bloqueio mmap)
- Contenção de descritor de arquivos em que uma linha de execução de baixa prioridade mantém a trava em um arquivo, impedindo a execução de uma linha de execução de alta prioridade
- Executar código crítico da interface em filas de trabalhos em que ele pode ser atrasado
- Transições de inatividade da CPU
- Geração de registros
- Atrasos de E/S
- Criação de processos desnecessários (por exemplo, transmissões
CONNECTIVITY_CHANGE
) - O cache de página está sendo usado em excesso devido à falta de memória
O tempo necessário para um determinado período de jitter pode ou não diminuir conforme a capacidade aumenta. Por exemplo, se um driver deixar as interrupções desativadas enquanto aguarda uma leitura em um barramento i2c, ele vai levar um tempo fixo, independentemente de a CPU estar a 384 MHz ou 2 GHz. Aumentar a capacidade não é uma solução viável para melhorar o desempenho quando o jitter está envolvido. Como resultado, processadores mais rápidos geralmente não melhoram o desempenho em situações com restrição de jitter.
Por fim, ao contrário da capacidade, o jitter está quase totalmente no domínio do fornecedor do sistema.
Consumo de memória
O consumo de memória é tradicionalmente culpado pelo desempenho ruim. Embora o consumo em si não seja um problema de desempenho, ele pode causar instabilidade por overhead do lowmemorykiller, reinicializações de serviço e uso excessivo do cache de página. Reduzir o consumo de memória pode evitar as causas diretas de desempenho ruim, mas pode haver outras melhorias direcionadas que também evitam essas causas. Por exemplo, fixar o framework para evitar que ele seja paginado quando for paginado logo em seguida.
Analisar o desempenho inicial do dispositivo
Começar com um sistema funcional, mas com baixo desempenho, e tentar corrigir o comportamento dele analisando casos individuais de desempenho ruim visíveis para o usuário não é uma estratégia sólida. Como a baixa performance geralmente não é facilmente reproduzível (ou seja, jitter) ou um problema do app, muitas variáveis no sistema completo impedem que essa estratégia seja eficaz. Como resultado, é muito fácil identificar incorretamente as causas e fazer melhorias menores, perdendo oportunidades sistêmicas de corrigir a performance em todo o sistema.
Em vez disso, use a seguinte abordagem geral ao exibir um novo dispositivo:
- Faça o sistema inicializar para a interface com todos os drivers em execução e algumas configurações básicas do governador de frequência. Se você mudar as configurações do governador de frequência, repita todas as etapas abaixo.
- Verifique se o kernel oferece suporte ao ponto de rastreamento
sched_blocked_reason
e a outros pontos de rastreamento no pipeline de exibição que indicam quando o frame é enviado à tela. - Faça rastros longos de todo o pipeline da interface (desde a recepção de entrada por um IRQ até a saída final) enquanto executa uma carga de trabalho leve e consistente (por exemplo, UiBench ou o teste de bola em TouchLatency).
- Corrija as quedas de frame detectadas na carga de trabalho leve e consistente.
- Repita as etapas 3 a 4 até conseguir executar sem frames perdidos por mais de 20 segundos de cada vez.
- Passe para outras fontes de instabilidade visíveis para o usuário.
Outras coisas simples que você pode fazer no início da configuração do dispositivo incluem:
- Confira se o kernel tem o patch de tracepoint sched_blocked_reason. Esse ponto de rastreamento é ativado com a categoria de rastreamento de programação no systrace e fornece a função responsável por suspender quando a linha de execução entra em suspensão ininterrupta. É fundamental para a análise de desempenho, porque o modo de suspensão ininterrupta é um indicador muito comum de jitter.
- Verifique se você tem rastreamento suficiente para a GPU e os pipelines de exibição. Em SOCs Qualcomm recentes, os pontos de rastreamento são ativados usando:
adb shell "echo 1 > /d/tracing/events/kgsl/enable"
adb shell "echo 1 > /d/tracing/events/mdss/enable"
Esses eventos permanecem ativados quando você executa o systrace para conferir mais
informações no rastro sobre o pipeline de exibição (MDSS) na
seção mdss_fb0
. Em SOCs da Qualcomm, não há informações
adicionais sobre a GPU na visualização padrão do systrace, mas os resultados estão
presentes no próprio trace. Para saber mais, consulte
Como entender
o systrace.
O que você quer desse tipo de rastreamento de exibição é um único evento que indica diretamente que um frame foi entregue à tela. A partir daí, é possível determinar se você atingiu o tempo de frame com sucesso.Se o evento Xn ocorrer menos de 16,7 ms após o evento Xn-1 (assumindo uma tela de 60 Hz), você saberá que não houve lentidão. Se o SOC não fornecer esses indicadores, trabalhe com o fornecedor para obtê-los. A depuração de jitter é extremamente difícil sem um sinal definitivo de conclusão do frame.
Usar comparativos sintéticos
Os comparativos de mercado sintéticos são úteis para garantir que a funcionalidade básica de um dispositivo esteja presente. No entanto, tratar os comparativos como um proxy para a performance percebida do dispositivo não é útil.
Com base nas experiências com SOCs, as diferenças no desempenho do benchmark sintético entre SOCs não estão correlacionadas com uma diferença semelhante no desempenho perceptível da interface (número de frames descartados, tempo de frame do percentil 99 etc.). Os comparativos de mercado sintéticos são comparativos de mercado exclusivos de capacidade. O jitter afeta a performance medida desses comparativos apenas roubando tempo da operação em massa do comparativo de mercado. Como resultado, as pontuações de comparação sintética são, em grande parte, irrelevantes como métrica de desempenho percebido pelo usuário.
Considere dois SOCs que executam o Benchmark X, que renderiza 1.000 frames da interface e informa o tempo total de renderização (quanto menor, melhor).
- O SOC 1 renderiza cada frame da comparação X em 10 ms e tem uma pontuação de 10.000.
- O SOC 2 renderiza 99% dos frames em 1 ms,mas 1% dos frames em 100 ms e pontua 19.900, uma pontuação muito melhor.
Se o comparativo for indicativo do desempenho real da interface, o SOC 2 não poderá ser usado. Considerando uma taxa de atualização de 60 Hz, o SOC 2 teria um frame instável a cada 1,5s de operação. Enquanto isso, o SOC 1 (o SOC mais lento de acordo com o Benchmark X) seria perfeitamente fluido.
Usar relatórios de bugs
Os relatórios de bugs às vezes são úteis para análise de desempenho, mas, como são pesados, raramente são úteis para depurar problemas esporádicos de instabilidade. Eles podem fornecer algumas dicas sobre o que o sistema estava fazendo em um determinado momento, especialmente se o problema ocorreu em uma transição de app (que é registrada em um relatório de bug). Os relatórios de bugs também podem indicar quando algo está mais amplo errado com o sistema, o que pode reduzir a capacidade efetiva dele, como limitação térmica ou fragmentação de memória.
Usar o TouchLatency
Vários exemplos de comportamento inadequado vêm do TouchLatency, que é a
carga de trabalho periódica preferida usada para o Pixel e o Pixel XL. Ela está disponível em
frameworks/base/tests/TouchLatency
e tem dois modos: latência de toque
e bola saltitante. Para alternar entre os modos, clique no botão no canto superior direito.
O teste da bola saltitante é exatamente tão simples quanto parece: uma bola salta pela tela para sempre, independentemente da entrada do usuário. Geralmente, esse é de longe o teste mais difícil de executar perfeitamente, mas quanto mais próximo ele estiver de ser executado sem frames perdidos, melhor será o dispositivo. O teste da bola de basquete é difícil porque é uma carga de trabalho trivial, mas perfeitamente consistente, que é executada em um clock muito baixo. Isso pressupõe que o dispositivo tenha um governador de frequência. Se o dispositivo estiver em execução com clocks fixos, reduza a CPU/GPU para o mínimo ao executar o teste da bola de basquete pela primeira vez. À medida que o sistema fica inativo e os relógios ficam mais próximos do modo inativo, o tempo de CPU/GPU necessário por frame aumenta. Você pode observar a bola e ver as coisas travando, e também poderá ver os frames perdidos no systrace.
Como a carga de trabalho é muito consistente, é possível identificar a maioria das fontes de jitter com muito mais facilidade do que na maioria das cargas de trabalho visíveis ao usuário, rastreando o que exatamente está sendo executado no sistema durante cada frame perdido, em vez do pipeline da interface. Os clocks mais baixos amplificam os efeitos do jitter, tornando mais provável que qualquer jitter cause uma queda de frame. Como resultado, quanto mais próximo o TouchLatency estiver de 60 QPS, menos provável será que você tenha comportamentos de sistema ruins que causem instabilidade esporádica e difícil de reproduzir em apps maiores.
Como o jitter geralmente (mas nem sempre) é independente da velocidade do relógio, use um teste que seja executado em relógios muito baixos para diagnosticar o jitter pelos seguintes motivos:
- Nem todo jitter é invariante em relação ao clockspeed. Muitas fontes apenas consomem tempo de CPU.
- O governador precisa aproximar o tempo médio de frame do prazo reduzindo o tempo, para que o tempo gasto na execução de trabalhos que não são da interface possa fazer com que ele chegue ao limite para rejeitar um frame.