Série 07 de otimização de desempenho do iOS|Um processo de convergência de causa raiz para solução de problemas de desempenho do iOS
O que é realmente difícil é primeiro determinar se o que você vê é o mesmo problema e, em seguida, eliminar um monte de pistas falsas, camada por camada.
Ao solucionar problemas de desempenho do iOS, o mais irritante é que a cena geralmente fica confusa desde o início.
O produto disse que a página inicial estava travada, o teste disse que havia queda de quadros na lista, o desenvolvimento achou que poderiam ser as imagens e o back-end suspeitou que a interface estava lenta. Todo mundo fala como uma pergunta, mas quando você começa a olhar para isso, muitas vezes descobre que eles não estão descrevendo a mesma coisa.
Depois de fazer esse tipo de trabalho diversas vezes, meu maior sentimento é: **A solução de problemas de desempenho começa com problemas de convergência, seguida pela análise de dados. **
Se o problema não for resolvido primeiro, quanto mais ferramentas você abrir, mais fácil será se desviar. Porque cada indicador que você vê parece conter informações, mas nem todas as informações são suficientes para chegar a uma conclusão.
O artigo a seguir não fala sobre “teoricamente como solucionar problemas”, mas escreve de acordo com o ritmo que acontecerá em um projeto real: Como o problema de queda de quadros no fluxo de informações da página inicial passou de “um pouco travado” passo a passo até o ponto em que pode ser corrigido manualmente?
Acerte a cena primeiro, caso contrário, todas as análises subsequentes serão perdidas.
A descrição inicial desse problema era muito comum:
- A página inicial não desliza suavemente *Alguns modelos são mais óbvios
- A versão mais recente parece ainda mais travada.
Esta descrição parece informação, mas na verdade é quase impossível usá-la diretamente para solução de problemas. Porque está misturado com pelo menos três camadas de coisas:
*O intervalo de páginas não está claro *O tipo de fenômeno não é claro *As condições para o reaparecimento não são claras
A descrição que realmente começou a funcionar foi finalmente reduzida a isto:
iPhone 13 Pro, iOS 18, conta de login A, inicialização a frio para entrar no fluxo de recomendação da página inicial, mude para a guia “Seguir” após o carregamento da primeira tela e, em seguida, volte para o fluxo de recomendação, deslize para cima 4 telas seguidas e continue a descartar quadros a partir da 3ª tela. O problema ocorre principalmente na disposição mista de gráficos e texto e na disposição mista de placas de vídeo.
A importância desta passagem não é que ela seja escrita como um caso de teste, mas que elimina muitas ambigüidades de uma só vez:
*É o cartão de fluxo recomendado na página inicial *É mais óbvio na segunda vez que você entra *está rolando e soltando quadros *A área onde as imagens e os textos se misturam é mais evidente.
O verdadeiro início da investigação muitas vezes começa com “acertar o escopo”. Sem este passo, todas as “análises” subsequentes podem ser apenas uma perseguição à própria imaginação.
Não se apresse em olhar as ferramentas primeiro, primeiro determine se isso é um congelamento ocasional ou uma taxa de quadros baixa contínua.
As quatro palavras “não deslizando suavemente” podem, na verdade, corresponder a problemas completamente diferentes.
A primeira coisa que fiz daquela vez foi gravar a tela e deslizá-la diversas vezes para caracterizar o fenômeno.
No final, esta é uma taxa de quadros baixa sustentada muito típica:
- Ao entrar em uma área de conteúdo específica, várias telas consecutivas não funcionarão perfeitamente. *é pesado durante toda a fase de rolamento
- A sensação subjetiva do usuário é que “esta seção não pode deslizar”
Esta distinção é muito importante.
Porque atrasos ocasionais geralmente se parecem mais com:
- Uma certa decodificação de imagem atingiu repentinamente o thread principal
- O tempo de inicialização de um objeto grande está errado
- Uma determinada passagem de layout ocasionalmente fica pesada
E os quadros baixos sustentados se parecem mais com:
- Fazendo trabalho repetitivo em cada quadro
- A própria estrutura celular é muito pesada
- Os resultados assíncronos são continuamente preenchidos durante a rolagem
- O intervalo de atualização do status da lista é muito grande
Se estes dois tipos de questões não forem separados no início, quaisquer dados posteriores serão facilmente mal interpretados.
O caminho de reprodução deve ser anotado, caso contrário as palavras “alterado” não terão significado.
Um dos sintomas mais comuns de problemas de desempenho é “Era óbvio agora, mas agora acabou”.
Então, nessa solução de problemas, o primeiro movimento que tomei foi estúpido, mas muito valioso: escrever o caminho de recorrência em etapas fixas.
O que foi resolvido naquela época foi:
- Encerre o aplicativo e reinicie a frio
- Faça login na conta A
- Entre no fluxo de recomendação da página inicial e aguarde até que a primeira tela fique totalmente estável.
- Mude para a guia “Seguir”
- Volte para o fluxo de recomendação
- Deslize 4 telas para cima em rápida sucessão
- Observe o desempenho da taxa de quadros e a ocupação do thread principal a partir da terceira tela
Lembre-se também do meio ambiente:
*Modelo do equipamento
- Versão do sistema *Condições da rede
- Nível de dados
- Se deve ativar o botão de depuração
A importância disto não é apenas facilitar a reprodução de outros, mas, mais importante ainda, facilitar a verificação subsequente.
Porque a frase mais inútil na otimização de desempenho é:
Eu me sinto melhor do que antes.
Não existe um caminho fixo e a palavra “evitar” não tem valor analítico. Pode ser apenas porque os dados são diferentes, a rede é diferente, a página não desliza para o mesmo parágrafo ou até mesmo o dedo não desliza tão rápido desta vez.
A primeira rodada de julgamento não encontra a causa raiz, mas apenas determina em qual link o problema está.
Quando começo a lidar com esse tipo de problema, quero perguntar:
- Qual linha de código é lenta?
- Qual função consome mais tempo?
- Qual biblioteca é o problema?
Mas numa investigação real, normalmente é muito cedo para perguntar.
O que fiz primeiro daquela vez foi a segmentação grosseira de links:
É um problema de dados ou de renderização?
Esta etapa é crítica, porque uma página inicial lenta pode facilmente fazer com que as pessoas duvidem instintivamente da interface.
Primeiro fiz uma verificação muito direta: Tente corrigir os resultados da rede tanto quanto possível, para que os dados ao entrar no fluxo de recomendação pela segunda vez venham de resultados existentes e não de alterações na rede.
O resultado é claro: Os dados retornaram e a rolagem ainda é pesada e constante.
Esta etapa pode basicamente eliminar a “rolagem travada devido à interface lenta” do problema principal.
A interface lenta afetará o tempo da primeira tela, mas não explica “após entrar pela segunda vez, determinado conteúdo passa a ter frames baixos contínuos”.
É o thread principal que está ocupado ou o trabalho em segundo plano continua retornando ao thread principal?
A próxima coisa a observar é o que o thread principal está fazendo quando o cartão está preso.
O objetivo desta etapa também é primeiro determinar a forma do problema:
- O tópico principal continua ocupado
- Ou os retornos de chamada em segundo plano são muito densos e o thread principal é interrompido um após o outro.
A última coisa que vi foi mais do primeiro: O tópico principal nem sempre está focado na fase de rolagem.
Isso mostra que a direção está começando a ficar clara:
*O foco não está na rede
- O foco não está em uma única tarefa grande
- O foco é mais como um link de exibição de lista em si é muito pesado
É um ponto único de exceção ou uma sobrecarga sistêmica?
Este é um julgamento que valorizo sempre.
Se apenas uma função se esgotar ocasionalmente por 200 ms, é fácil de manusear, basta capturá-la. Mas não foi esse o caso daquela vez.
O que é realmente problemático naquela época é que não havia nenhum ponto tão escandaloso a ponto de ser “um verdadeiro assassino à primeira vista”, mas muitas pequenas despesas que não eram exageradas foram acumuladas e todas caíram no caminho crítico de rolagem.
Esse tipo de problema é o mais irritante, porque não possui uma pilha particularmente clara como o crash. É mais como se o sistema estivesse lhe dizendo que cada pequena decisão que foi “escrita assim” no passado agora está sendo cobrada em conjunto.
Só vale a pena abrir Instrumentos neste momento, e isso deve ser feito com desconfiança.
Nunca gostei muito da abordagem “abra todas as ferramentas primeiro e depois fale” para solucionar problemas. É tão fácil usar ferramentas como substitutos do pensamento.
Antes de entrar na Instruments naquela época, eu realmente tinha várias dúvidas:
Suspeita 1: O link da imagem coloca no período contínuo trabalhos que não deveriam ser colocados no período contínuo.
Este é o suspeito mais comum em cenários de fluxo de informações.
Especialmente:
- O tamanho da imagem da capa não é uniforme
- Arranjo misto de gráficos, texto e placas de vídeo
- O ritmo de preenchimento da imagem é instável
- Antes da exibição, também são feitos processamentos como cantos arredondados, sombras e dimensionamento.
Se todo esse trabalho realmente acontecer durante a rolagem, será difícil estabilizar a taxa de quadros.
Suspeita 2: A apresentação da célula foi preparada tarde demais
Muitas listas parecem “logicamente claras” a princípio, mas quando você realmente percorre o equipamento, um problema antigo será exposto:
Muito trabalho de preparação de exibição é feito quando a célula está prestes a ser exibida.
Por exemplo:
- Montagem de rich text *Corte de redação
- Formatação de hora
- Cálculo de altitude
- Julgamento do status da IU
- Preparação de objetos enterrados
Cada item não é tão grande por si só, mas uma vez que todos sejam espremidos no caminho crítico de rolagem, ele passará de “aceitável” para “a lista inteira é pesada”.
Suspeita 3: O intervalo de atualização de status é muito grande
Esta situação é particularmente comum em arquiteturas reativas.
Superficialmente, é apenas uma mudança de status do cartão, mas o que realmente é acionado é:
- Religação em nível de seção
- Muitas diferenças locais
- Acoplamento de relatórios de exposição e atualização da IU
- Os retornos de chamada de pré-carregamento retornam frequentemente ao thread principal
Este tipo de problema é o mais incômodo, pois pode não causar necessariamente um pico particularmente alto em uma determinada função, mas a experiência geral será sempre ruim.
O que realmente restringe a direção são algumas rodadas de experimentos baratos.
Dessa vez, reduzimos o problema a algumas rodadas de experimentos muito simples.
Experimento 1: Substitua todas as imagens por imagens de espaço reservado
Este experimento é muito rudimentar, mas extremamente útil.
Depois que a imagem é substituída, a rolagem obviamente volta. Esta etapa não explica diretamente que a causa raiz é “muitas fotos”, mas é suficiente para mostrar que os links de imagens devem ser uma das direções principais.
Se você continuar perguntando neste momento, não será mais um “sim” geral, mas mais específico:
- Se a decodificação recai no thread principal
- A estratégia de tamanho é inconsistente?
- O preenchimento da imagem causa re-layout?
- Há muito processamento adicional feito antes da exibição?
Experiência 2: Pré-processar parte do texto dinâmico da célula
Com esta mudança, a rolagem também foi significativamente melhorada.
Isso mostra que a outra direção também é verdadeira: Na verdade, é demasiado tarde para preparar a lista antes de esta ser apresentada, e há mais do que uma ou duas destas tarefas.
Em outras palavras, o problema é que o link da imagem e o link da apresentação são duplicados juntos.
Experimento 3: desative temporariamente essas ações de borda, como exposição, pré-carregamento e reprodução automática
Após esta rodada de experimentos, a taxa de quadros continuou a se estabilizar.
Neste ponto o problema é basicamente claro: É o fluxo de informações da página inicial que realiza muito trabalho do tipo “apenas faça” durante o estágio de rolagem.
Essas tarefas geralmente parecem razoáveis por si só:
*As fotos precisam ser preenchidas
- A exposição deve ser relatada
- O pré-carregamento precisa ser feito *A placa de vídeo precisa ser aquecida
Mas quando eles acontecem juntos no caminho crítico de rolagem, eles derrubam toda a lista.
No final das contas, a verdadeira causa raiz é um conjunto de decisões erradas
Quando finalmente pousou, o verdadeiro problema provavelmente foi esta combinação de socos:
- A estratégia de tamanho da imagem não é uniforme, resultando em custos de processamento antes da exibição
- Algumas imagens são decodificadas tarde demais
- O trabalho de preparação da exibição do celular é feito no local no tópico principal
- Os retornos de chamada de exposição e pré-carregamento são muito densos durante o período de rolagem
- Listar alterações de estado local aciona um intervalo de atualização maior do que o esperado
É assim que muitos problemas de desempenho realmente se parecem.
Não é o fim da “linha 357 encontrada”, Mas no final, você descobrirá que o desenho de várias camadas não é suficientemente restrito e, finalmente, será assentado junto na fase de laminação.
Portanto, cada vez mais não gosto do argumento de que “deve haver uma função-chave para problemas de desempenho”. É claro que isso acontece em projetos reais, mas na maioria das vezes é “um conjunto de formas ruins que estão acumuladas há muito tempo”.
A fase de verificação tem mais medo de sentimentos subjetivos. Você deve voltar ao mesmo caminho para comparar.
Após a modificação, o problema não foi resolvido diretamente, mas o caminho de recorrência original foi retomado.
O mesmo dispositivo, a mesma conta, o mesmo método de troca, o mesmo deslizamento para a área de conteúdo e veja:
- Se as quedas de quadros ainda ocorrem de forma estável?
- O tópico principal ainda está focado no mesmo período de tempo?
- O preenchimento de imagem e os comportamentos de borda ainda competem com a rolagem para competir com o thread principal?
Nesta etapa, também dei uma olhada nos efeitos colaterais:
- Após o link da imagem ser conectado, há algum aumento significativo na memória?
- Se você fizer muito pré-processamento, a primeira tela ficará mais lenta?
- O relatório de exposição está atrasado. As estatísticas serão afetadas?
Um erro frequentemente cometido na otimização de desempenho: Eles se concentram apenas em “se um indicador parece bom”, mas não analisam se ele substituiu outros problemas.
Mas no trabalho real, a otimização é sempre uma questão de troca na experiência geral. A troca aceitável é chamada de otimização, a troca de outros problemas não é chamada de otimização.
É mais provável que esse tipo de artigo seja escrito como conversa fiada, mas também é o local mais realista para solução de problemas de desempenho.
Muitos artigos de resumo acabarão escrevendo:
*Defina o problema primeiro
- Para estabelecer uma hipótese
- Para verificar os resultados
Estas palavras são certamente verdadeiras, mas podem facilmente transformar-se em disparates correctos.
A diferença na experiência real não é se você consegue dizer essas palavras, mas se você vivenciou os seguintes momentos:
- No início todos disseram que parecia o mesmo problema, mas na verdade não era o mesmo problema.
- A direção que mais se parece com a causa raiz à primeira vista é apenas uma pista falsa no final.
- Cada indicador na ferramenta parece anormal, mas existe apenas uma linha principal real
- A resposta final é um conjunto de pequenas despesas gerais que aumentam com o tempo
Depois de fazer essas coisas algumas vezes, você naturalmente entenderá uma coisa:
**O valor real da solução de problemas de desempenho é “posso transformar uma cena caótica em um link viável?” **Isso não pode ser feito, quanto mais ferramentas houver, mais confuso será. Ao fazer isso, você pode chegar cada vez mais perto da causa raiz de muitos problemas sem passar por todos os gráficos.
Quando olho para questões de desempenho agora, o que mais me importa é se conseguiremos fazer com que a equipe chegue a um acordo sobre o seguinte o mais rápido possível:
Estamos agora investigando o mesmo problema e já sabemos em qual link ele se enquadra principalmente.
Desde que isto seja estabelecido, os reparos subsequentes geralmente não são muito confusos. O que é realmente difícil é sempre reunir o problema numa forma que possa ser resolvida.
What to read next
Want more posts about iOS Performance Optimization?
Posts in the same category are usually the best next step for reading more on this topic.
View same categoryWant to keep following #iOS?
Tags are useful for related tools, specific problems, and similar troubleshooting notes.
View same tagWant to explore another direction?
If you are not sure what to read next, return to the homepage and start from categories, topics, or latest updates.
Back home