Avaliando o Desempenho

Use Simpleperf para avaliar o desempenho de um dispositivo. Simpleperf é uma ferramenta de criação de perfil nativa para aplicativos e processos nativos no Android. Use o CPU Profiler para inspecionar o uso da CPU do aplicativo e a atividade de thread em tempo real.

Existem dois indicadores de desempenho visíveis ao usuário:

  • Desempenho previsível e perceptível . A interface do usuário (UI) descarta quadros ou renderiza consistentemente a 60FPS? O áudio é reproduzido sem artefatos ou estouros? Quanto tempo dura o atraso entre o usuário tocar na tela e o efeito exibido na tela?
  • Período de tempo necessário para operações mais longas (como abertura de aplicativos).

A primeira é mais perceptível que a segunda. Os usuários normalmente percebem a instabilidade, mas não poderão dizer o tempo de inicialização do aplicativo de 500 ms versus 600 ms, a menos que estejam olhando para 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 da interface do usuário é a coisa mais importante no sistema, além do necessário para manter o pipeline da interface do usuário funcional. Isso significa que o pipeline da interface do usuário deve antecipar qualquer outro trabalho que não seja necessário para a interface do usuário fluida. Para manter uma interface do usuário fluida, a sincronização em segundo plano, a entrega de notificações e trabalhos semelhantes devem ser atrasados ​​se o trabalho da interface do usuário puder ser executado. É aceitável negociar o desempenho de operações mais longas (tempo de execução HDR+, inicialização do aplicativo etc.) para manter uma interface do usuário fluida.

Capacidade vs jitter

Ao considerar o desempenho do dispositivo, a capacidade e o jitter são duas métricas significativas.

Capacidade

Capacidade é a quantidade total de algum recurso que o dispositivo possui em um determinado período de tempo. Isso pode ser recursos de CPU, recursos de GPU, recursos de E/S, recursos de rede, largura de banda de memória ou qualquer métrica semelhante. Ao examinar o desempenho de todo o sistema, 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 nesse dispositivo provavelmente são fixas).

A capacidade de um sistema varia com base nos recursos de computação online. Alterar a frequência de CPU/GPU é o principal meio de alterar a capacidade, mas existem outros, como alterar o número de núcleos de CPU online. Assim, a capacidade de um sistema corresponde ao consumo de energia; a mudança de capacidade sempre resulta em uma mudança semelhante no consumo de energia.

A capacidade necessária em um determinado momento é determinada predominantemente pelo aplicativo em execução. Como resultado, a plataforma pode fazer pouco para ajustar a capacidade necessária para uma determinada carga de trabalho, e os meios para fazer isso são limitados a melhorias de tempo de execução (estrutura Android, ART, Bionic, compilador/drivers de GPU, kernel).

Tremor

Embora a capacidade necessária para uma carga de trabalho seja fácil de ver, o jitter é um conceito mais nebuloso. Para uma boa introdução ao jitter como um impedimento para sistemas rápidos, consulte O CASO DA FALTA DE DESEMPENHO DO SUPERCOMPUTADOR: ATINGINDO O DESEMPENHO ÓTIMO NOS 8.192 PROCESSADORES DE ASCl Q . (É uma investigação de por que o supercomputador ASCI Q não atingiu o desempenho esperado e é uma ótima introdução à otimização de grandes sistemas.)

Esta página usa o termo jitter para descrever o que o documento ASCI Q chama de ruído . Jitter é o comportamento aleatório do sistema que impede a execução de um trabalho perceptível. Muitas vezes, é um trabalho que deve ser executado, mas pode não ter requisitos rigorosos de tempo que fazem com que seja executado em um determinado momento. Por ser 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 comumente usadas para diagnosticar as causas do jitter (como rastreamento ou registro) podem apresentar seu próprio jitter.

Fontes de jitter experimentadas em implementações do mundo real do Android incluem:

  • Atraso do programador
  • Manipuladores de interrupção
  • Código de driver em execução por muito tempo com preempção ou interrupções desabilitadas
  • Softirqs de longa duração
  • Contenção de bloqueio (aplicativo, estrutura, driver de kernel, bloqueio de fichário, bloqueio de mmap)
  • Contenção do descritor de arquivo em que um thread de baixa prioridade mantém o bloqueio em um arquivo, impedindo a execução de um thread de alta prioridade
  • Executando código crítico da interface do usuário em filas de trabalho onde pode ser atrasado
  • Transições ociosas da CPU
  • Exploração madeireira
  • Atrasos de E/S
  • Criação desnecessária de processos (por exemplo, transmissões CONNECTIVITY_CHANGE)
  • Thrashing de cache de página causado por memória livre insuficiente

A quantidade de tempo necessária para um determinado período de jitter pode ou não diminuir à medida que a capacidade aumenta. Por exemplo, se um driver deixar as interrupções desabilitadas enquanto aguarda uma leitura em um barramento i2c, levará um tempo fixo, independentemente de a CPU estar em 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 melhorarão o desempenho em situações com restrição de jitter.

Finalmente, ao contrário da capacidade, o jitter está quase inteiramente dentro do domínio do fornecedor do sistema.

Consumo de memória

O consumo de memória é tradicionalmente culpado pelo baixo desempenho. Embora o consumo em si não seja um problema de desempenho, ele pode causar instabilidade por meio de sobrecarga de baixo nível de memória, reinicializações de serviço e sobrecarga de cache de página. Reduzir o consumo de memória pode evitar as causas diretas do baixo desempenho, mas pode haver outras melhorias direcionadas que evitam essas causas também (por exemplo, fixar a estrutura para evitar que ela seja paginada quando for paginada logo depois).

Analisando o desempenho inicial do dispositivo

Partir de um sistema funcional, mas com baixo desempenho, e tentar corrigir o comportamento do sistema observando casos individuais de baixo desempenho visível ao usuário não é uma estratégia sólida. Como o desempenho ruim geralmente não é facilmente reproduzível (ou seja, jitter) ou um problema de aplicativo, muitas variáveis ​​no sistema completo impedem que essa estratégia seja eficaz. Como resultado, é muito fácil identificar incorretamente as causas e fazer pequenas melhorias, perdendo oportunidades sistêmicas para corrigir o desempenho em todo o sistema.

Em vez disso, use a seguinte abordagem geral ao criar um novo dispositivo:

  1. Faça a inicialização do sistema na interface do usuário com todos os drivers em execução e algumas configurações básicas do regulador de frequência (se você alterar as configurações do regulador de frequência, repita todas as etapas abaixo).
  2. Certifique-se de que o kernel suporte o sched_blocked_reason , bem como outros tracepoints no pipeline de exibição que indicam quando o quadro é entregue ao monitor.
  3. Faça longos rastreamentos de todo o pipeline da interface do usuário (desde o recebimento de entrada por meio de um IRQ até a varredura final) enquanto executa uma carga de trabalho leve e consistente (por exemplo, UiBench ou o teste de bola no TouchLatency) .
  4. Corrija as quedas de quadros detectadas na carga de trabalho leve e consistente.
  5. Repita as etapas de 3 a 4 até poder executar com zero quadros descartados por mais de 20 segundos de cada vez.
  6. Passe para outras fontes de instabilidade visíveis ao usuário.

Outras coisas simples que você pode fazer no início da apresentação do dispositivo incluem:

  • Certifique-se de que seu kernel tenha o patch de tracepoint sched_blocked_reason . Esse tracepoint é habilitado com a categoria de rastreamento sched no systrace e fornece a função responsável por dormir quando esse encadeamento entra em suspensão ininterrupta. É fundamental para a análise de desempenho porque o sono ininterrupto é um indicador muito comum de jitter.
  • Certifique-se de ter rastreamento suficiente para a GPU e os pipelines de exibição. Em SOCs recentes da Qualcomm, os pontos de rastreamento são habilitados usando:
  • adb shell "echo 1 > /d/tracing/events/kgsl/enable"
    adb shell "echo 1 > /d/tracing/events/mdss/enable"
    

    Esses eventos permanecem habilitados quando você executa o systrace para que você possa ver informações adicionais no rastreamento sobre o pipeline de exibição (MDSS) na seção mdss_fb0 . Nos SOCs da Qualcomm, você não verá nenhuma informação adicional sobre a GPU na visualização padrão do systrace, mas os resultados estão presentes no próprio rastreamento (para obter detalhes, consulte Entendendo o systrace ).

    O que você deseja com esse tipo de rastreamento de exibição é um único evento que indique diretamente que um quadro foi entregue ao monitor. A partir daí, você pode determinar se atingiu seu frame time com sucesso; se o evento X n ocorrer menos de 16,7 ms após o evento X n-1 (assumindo uma exibição de 60 Hz), você saberá que não sofreu instabilidade. Se seu SOC não fornecer esses sinais, trabalhe com seu fornecedor para obtê-los. A depuração do jitter é extremamente difícil sem um sinal definitivo de conclusão do quadro.

Usando benchmarks sintéticos

Os benchmarks sintéticos são úteis para garantir que a funcionalidade básica de um dispositivo esteja presente. No entanto, tratar os benchmarks como um proxy para o desempenho percebido do dispositivo não é útil.

Com base em experiências com SOCs, as diferenças no desempenho de benchmark sintético entre SOCs não estão correlacionadas com uma diferença semelhante no desempenho perceptível da interface do usuário (número de quadros descartados, 99º percentil de tempo de quadro etc.). Os benchmarks sintéticos são benchmarks somente de capacidade; jitter afeta o desempenho medido desses benchmarks apenas roubando tempo da operação em massa do benchmark. Como resultado, as pontuações de benchmark sintéticas são, em sua maioria, irrelevantes como métrica de desempenho percebido pelo usuário.

Considere dois SOCs executando o Benchmark X que renderiza 1.000 quadros de interface do usuário e relata o tempo total de renderização (menor pontuação é melhor).

  • O SOC 1 renderiza cada quadro do Benchmark X em 10 ms e pontua 10.000.
  • O SOC 2 renderiza 99% dos quadros em 1 ms, mas 1% dos quadros em 100 ms e pontua 19.900, uma pontuação dramaticamente melhor.

Se o benchmark for indicativo do desempenho real da interface do usuário, o SOC 2 não poderá ser usado. Assumindo uma taxa de atualização de 60Hz, o SOC 2 teria um quadro 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.

Usando relatórios de bugs

Relatórios de bugs às vezes são úteis para análise de desempenho, mas por serem tã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 a instabilidade ocorreu em torno de uma transição de aplicativo (que é registrada em um relatório de bug). Os relatórios de bugs também podem indicar quando algo está mais errado com o sistema que pode reduzir sua capacidade efetiva (como limitação térmica ou fragmentação de memória).

Usando TouchLatency

Vários exemplos de mau comportamento vêm do TouchLatency, que é a carga de trabalho periódica preferida usada para o Pixel e o Pixel XL. Está disponível em frameworks/base/tests/TouchLatency e tem dois modos: latência de toque e bola quicando (para alternar entre os modos, clique no botão no canto superior direito).

O teste da bola quicando é exatamente tão simples quanto parece: uma bola quica pela tela para sempre, independentemente da entrada do usuário. Geralmente, também é de longe o teste mais difícil de executar perfeitamente, mas quanto mais próximo estiver de executar sem nenhum quadro perdido, melhor será o seu dispositivo. O teste da bola saltitante é 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 regulador de frequência; se o dispositivo estiver executando com clocks fixos, reduza o clock da CPU/GPU para quase o mínimo ao executar o teste da bola quicando pela primeira vez). À medida que o sistema silencia e os relógios caem mais perto do estado inativo, o tempo necessário de CPU/GPU por quadro aumenta. Você pode assistir a bola e ver as coisas travando, e também poderá ver os quadros perdidos no systrace.

Como a carga de trabalho é tão consistente, você pode 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 quadro perdido em vez do pipeline da interface do usuário. Os clocks mais baixos amplificam os efeitos do jitter, tornando mais provável que qualquer jitter cause uma queda de quadro. Como resultado, quanto mais próximo o TouchLatency estiver de 60FPS, menor será a probabilidade de você ter comportamentos ruins do sistema que causam instabilidade esporádica e difícil de reproduzir em aplicativos maiores.

Como o jitter é frequentemente (mas nem sempre) invariante na velocidade do clock, use um teste que seja executado em clocks muito baixos para diagnosticar o jitter pelos seguintes motivos:

  • Nem todo jitter é invariante na velocidade do clock; muitas fontes apenas consomem tempo de CPU.
  • O controlador deve obter o tempo médio de quadro próximo ao prazo final, reduzindo o tempo, para que o tempo gasto executando trabalho não relacionado à interface do usuário possa levá-lo ao limite para descartar um quadro.