
Como evitar problemas de desempenho com a predefinição Core Animation, o que usar para rastrear fragmentos de código e com quais funções reduzir o compartilhamento de operações computacionais no aplicativo de 26% para 0,6% - leia a segunda parte do artigo com base nos materiais do relatório de Luke Parkham na conferência MBLT DEV do ano passado . A primeira parte do artigo está disponível aqui .
Sob o gato, não apenas dicas úteis, mas também os mais recentes ingressos para madrugadores do MBLT DEV 2018 - você pode comprá-los somente hoje.
Animação principal
A Animação central (CA) é uma predefinição no criador de perfil que usa medições FPS (quadros por segundo) para verificar se as animações estão atrasadas ou não. Freqüentemente, mesmo se áreas problemáticas do aplicativo forem encontradas, as dificuldades de desempenho permanecem. O motivo é que, ao trabalhar com estruturas de interface do usuário, o UIView é usado, mas uma instância do CATransaction é criada sob o capô (ou o sistema faz isso por si só), e todas essas instruções são enviadas ao servidor para processamento. O servidor para renderização é responsável pela criação da animação. Se uma animação é executada usando um UIView, por exemplo, o método da classe animate(withDuration:animations:)
, ela é processada pelo servidor de renderização, que é considerado um thread separado e funciona com todas as animações no aplicativo.
Você pode fazer com que o servidor de renderização trabalhe lentamente, para que não apareça no Time Profiler, mas ainda diminuirá a velocidade do aplicativo. Aqui está o que parece:


No topo está o sensor de taxa de quadros. Abaixo está a parte mais importante - opções de depuração. Existem dois parâmetros principais e fáceis de configurar. O primeiro são as color blended layers
. Consertá-lo é bem simples. De fato, surgem problemas no iMessage, o aplicativo amado da Apple.

Vermelho indica áreas com fundo branco. Eles se sobrepõem a outro fundo branco e, por algum motivo, parecem transparentes. Acontece que o programa mistura essas cores entre si - branco com branco, e o resultado é novamente branco. Como resultado, para cada pixel marcado em vermelho, são realizados cálculos extras que não trazem nenhum benefício - um fundo branco é obtido.
Regra nº 3
Torne as camadas opacas sempre que possível - desde que tenham as mesmas cores que se sobrepõem. A camada tem a propriedade opacity
, que deve ser definida como unidade. Sempre verifique se a cor do plano de fundo está definida e se está opaca.

Renderização fora da tela
A próxima opção é a renderização fora da tela. Se você ativar esta função, as seções serão destacadas em amarelo.
A conveniência do Core Animation é a capacidade de visualizar outros aplicativos. Você pode ativar as opções, iniciar o aplicativo e ver o que está errado. Na tela do Instagram, na parte superior, existem pequenos círculos amarelos nos quais as histórias do usuário são exibidas.

Por exemplo, no iPhone 6s, eles carregam bem devagar. E se você olhar para eles no iPhone 5 ou no modelo antigo do iPod, o download será ainda mais lento. Isso se deve ao fato de que a renderização intra-sistema é muito pior que a mistura alfa. Carrega a GPU. Como resultado, o dispositivo precisa executar constantemente cálculos adicionais entre o processador gráfico e o processador central, para que haja atrasos adicionais que possam ser evitados na maioria dos casos.
Regra nº 4
Não use o parâmetro cornerRadius
. O uso de viewLayer.cornerRadius
resulta na renderização fora da tela. Em vez disso, você pode usar a classe UIBezierPath
, bem como algo semelhante ao trabalho com o CGBitMap, como foi o caso com a decodificação JPEG antes. Nesse caso, o UIGraphics context
.

Este é outro método de instância da classe UIImage. Aqui você pode definir o tamanho e fazer cantos arredondados. Bezierpath
é usado para destacar uma área da imagem. Em seguida, o fragmento é retornado do UIImageContext. Assim, obtemos a imagem final com cantos arredondados em vez de arredondar os quadros nos quais a imagem será inserida.

Na página GIF - Twitter. Imagem mostrada em tempo real. A página deve abrir e fornecer informações, mas o texto e outros elementos da tela passaram pela renderização fora da tela, de modo que as animações se movem muito lentamente.
Regra nº 5
A propriedade da classe shouldRasterize do conjunto CALayer permite armazenar em cache as texturas que foram renderizadas. Melhor evitá-lo. Se shouldRasterize
não for usado por um determinado número de milissegundos, ele deixará o cache e oferecerá a renderização de cada quadro. Portanto, isso cria mais problemas do que benefícios.
Acelerar
- Evite renderização fora da tela e mistura de camadas transparentes.
- Use-os somente quando não puder ficar sem eles. A renderização fora da tela aparece devido à presença de sombras, cantos arredondados e assim por diante.
- Torne as imagens opacas.
- Não use cornerRadius, use curvas de Bezier.
- Ao trabalhar com texto, não use a propriedade layer.shadow, substitua-a pelo NSShadow.
Rastreio de atividade
O rastreamento de atividades é semelhante ao que o Time Profiler faz, mas em uma escala menor. Ele permite que você considere fluxos e sua interação entre si.
Regra nº 6
Use o Rastreio do sistema para rastrear períodos para eventos específicos. Você pode criar uma maneira de rastrear eventos ou seções de código e ver quanto tempo eles levam no trabalho real do aplicativo. O Rastreio do Sistema fornece informações sobre o que está acontecendo no sistema:
- O “Sing Post” sinaliza que algo importante está acontecendo.
- Marcas são eventos únicos que valem a pena assistir, por exemplo, a aparência de uma animação.
- Pelo intervalo do evento, você pode acompanhar quanto tempo a decodificação leva.

Portanto, o programa mostra como o código interage com o resto do sistema.
Na tela, há um exemplo de criação de um modelo de rastreamento do sistema:
- 1 - upload de imagem
- 2 - decodificação de imagem
- 3 - animações de inclinação.
Você precisa adicionar algumas opções para entender qual será a cor. Normalmente, eles recebem números, como 1 ou 2, e ficam vermelhos ou verdes, dependendo da configuração. No Objective-C, você precisa escrever o comando #import
para kdebug_signpost
. No Swift, eles já estão disponíveis.

Então você precisa chamar essa função kdebug_signpost
ou kdebug_signpost_start
e kdebug_signpost_end
.

Indicamos 3 eventos, juntamente com os números escritos no código, e um elemento-chave para cada evento específico. O último número indica a cor. Por exemplo, 2 é vermelho.
A seguir, são eventos importantes, objetos especiais. Um diagrama simplificado é descrito no projeto de teste de Luke no Swift.
A captura de tela mostra como ficará quando você iniciar o rastreamento. Inicialmente, quando o aplicativo é iniciado, o programa não fornece informações, mas assim que o aplicativo falha, veremos os cálculos.

O download de imagens leva cerca de 200 milissegundos. Depois vem a decodificação, que leva cerca de 40 milissegundos. É útil acompanhar esses dados se houver muitas operações no aplicativo. Você pode listar eventos no programa e, em seguida, observar e receber informações sobre quanto tempo é necessário para sua implementação e como eles interagem.
Ferramentas adicionais
Na tela - projeto de AR para gravação em câmera lenta de um smartphone. O aplicativo é semelhante ao Snapchat. Ele usou o efeito de retoque de fotos e, para cada quadro, 26,4% de todas as operações de computação foram gastas nele. Por esse motivo, a câmera estava filmando lentamente, cerca de 10 quadros por segundo. Ao estudar, vemos que a linha superior fez a maior parte do trabalho. Ela era responsável pelo envio ativo de dados. Se você examinar as causas do subsidência de desempenho, perceberá que o ponto é o uso intenso do NSDispatchData
.

Essa subclasse de NSData
recebe apenas uma sequência de bytes usando o método getBytes
em um determinado intervalo e a passa para outro local. Parece ser tão simples, no entanto, tudo o que esse método faz internamente leva 18% de 26% dos cálculos.

Regra nº 1
Use
NSData
e
getBytes
. Essa é uma operação simples no Objective-C, mas se causar problemas no sistema, você deve mudar para uma função de nível inferior na planície C. Como o problema é obter valores flutuantes, você pode usar a função de memória de cópia. Com ele, você pode mover um dado para outro local. Isso economiza bastante o poder de computação do dispositivo.
O NSData possui muitas operações. No exemplo, o código fonte é destacado em vermelho. Usando a função
getBytes
,
getBytes
pode converter dados em números de ponto flutuante. O método para copiar a memória é quase o mesmo. É bastante simples e executa uma ordem de magnitude menos operações.

Se você alterar a abordagem e iniciar o aplicativo novamente, é possível observar que a porcentagem de operações computacionais gastas na alteração da foto diminuiu de 26% para 0,6%. E isso ocorre devido à alteração de apenas uma linha de código sobre cópia de memória. A taxa de quadros aumentou significativamente.

Regra nº 2
Evite a sobreposição de pixels um ao outro ao criar um aplicativo que tenha renderização.
Na maioria dos casos, isso ocorrerá em uma frequência acima de 60 quadros por segundo. Usando o CADisplayLink
, você pode retardar a atualização da interface do usuário. Há um parâmetro preferredFramesPerSecond
. Aplica-se apenas ao iOS 10 e posterior. Para sistemas mais antigos, você deve fazer isso manualmente. Ao trabalhar em novas versões do iOS, você pode definir o número desejado de quadros por segundo. Na maioria dos casos, 15 quadros por segundo, aproximadamente, para não desperdiçar energia de computação e não adicionar trabalho desnecessário ao aplicativo.

Regra nº 3
Ao trabalhar com o Objective-C, é útil usar o cache de ponteiros IMP (ponteiros para a implementação de métodos). Quando o método
under the hood
é chamado em Objective-C, a função
objc_msgSend()
é
objc_msgSend()
. Se o rastreamento mostrar que a chamada demora muito tempo, você pode se livrar dela. De fato, este é um repositório de cache com ponteiros para uma função; eles podem receber os nomes de alguns métodos. Portanto, em vez de realizar essa pesquisa a cada vez, vale a pena fazer o cache da seguinte maneira: coloque os ponteiros de função no cache e chame-os diretamente. Isso geralmente acontece duas vezes mais rápido.

Se não houver ponteiro, o método será chamado usando o comando methodForSelector. Para chamar esse método de ajuda da função de chamada usual, é necessário inserir o nome do objeto no seletor, que produz os resultados da pesquisa. Talvez essa não seja a maneira mais conveniente, mas rápida.
Regra nº 4
Não use o ARC (contagem automática de referência). O ARC adiciona muito trabalho extra.
Quando o ARC é ativado, o próprio compilador dispersa retain
/ release
nos lugares certos. No entanto, se houver lugares onde retain
e release
levam muito tempo, considere a possibilidade de descartar o ARC. Faça isso se a otimização for necessária, pois levará muito tempo.
Às vezes, vale a pena abandonar o Swift, principalmente se o desempenho do aplicativo for sensível a mudanças. O Swift possui alguns recursos muito interessantes, mas para executar tarefas ainda pequenas, é necessário escrever muitas linhas de código para obter um alto nível de funcionalidade.
Materiais úteis
A primeira parte do artigo está disponível aqui . Para mais informações sobre o tópico, Luke Parham recomenda ler o livro “ iOS e MacOS: ajuste de desempenho ” e assistir a seus tutoriais .
A gravação em vídeo do relatório de Luke no MBLT DEV 2017 agora é de domínio público:
Mais conhecimentos e dicas no MBLT DEV 2018
Os primeiros oradores anunciaram:
- John C. Fox, da Netflix, fala sobre localização de alta qualidade, trabalhando com condições de rede agressivas e testes A / B.
- Krzysztof Zabłocki, The New York Times, está preparando um relatório sobre padrões no iOS.
- Laura Morinigo, especialista em desenvolvedores do Google, fala sobre as melhores práticas do Firebase.
Os últimos ingressos para madrugadores voarão hoje.
