Como economizar recursos no navegador e não quebrar a web. Relatório Yandex

Apesar do crescimento do desempenho do dispositivo, a web está se tornando cada vez mais exigente em memória e processador. A renderização adequada e a alocação inteligente de recursos entre as guias são uma parte importante da solução desse problema. Konstantin Kramlih PurplePowder dedicou seu discurso na conferência “I Frontend” a algoritmos que melhoram a produtividade e economizam recursos, tanto no projeto Chromium quanto no Yandex.Browser.

Alguns deles - por exemplo, a tecnologia Hibernate - já classificamos em um post separado . O relatório Bones cobre o problema de maneira mais ampla: não apenas do ponto de vista da troca de guias, mas também levando em consideração os métodos de renderização de conteúdo, blocos e camadas de página.

No final, os desenvolvedores de interface da Web podem aprender a identificar e resolver problemas de desempenho do site.


- Meu nome é Kostya, sou o chefe do grupo de desenvolvimento de componentes internos da equipe Yandex.Browser. No navegador, faço coisas diferentes há pouco mais de cinco anos: de toda decodificação em um navegador, de todos os vídeos HTML5 a renderização, renderização e outros processos semelhantes.

Nos últimos um ano e meio a dois anos, participei de projetos relacionados à economia de recursos no navegador: CPU, memória, bateria.

Você diz - Kostya, no quintal de 2019, onde estão os problemas com recursos? Você pode comprar qualquer dispositivo que desejar com quaisquer recursos. Mas se voltarmos para as estatísticas abertas do Mozilla, veremos que metade dos usuários tem 4 GB de memória ou menos. E muitos usuários que têm um ou dois núcleos físicos representam uma proporção considerável de seu público. Neste mundo em que vivemos.



Quantos de vocês costumam ver esta guia? É exatamente o que acontece no caso de usuários comuns que têm pouca memória RAM e computadores antigos.



O que fazer O problema não é segredo para ninguém, eles começaram a lutar ativamente com ele cerca de três anos atrás. Desde então, as porcas foram gradualmente apertadas de várias maneiras. Deixe-me mostrar um exemplo que foi publicado no Stack Overflow em 2016. Aqui está um trecho bem simples. Isso apenas atualiza o título e define o tempo decorrido desde o último lançamento desta função. O que deve ser obtido idealmente? Cada 100 ms no título deve ser escrito + -100. Que sorte.



Mas e se abrirmos e fizermos isso? Alguém já encontrou isso? A pergunta foi para Stack Overflow: que diabos o Cookie Clicker para de funcionar nas minhas guias de segundo plano? Essa foi uma das primeiras iniciativas do Chromium destinadas a reduzir o uso da CPU no navegador. A ideia era que, se o usuário não usa a guia agora, ele não precisa mais agora - vamos colocar JS nela.

O navegador tenta manter a carga da CPU nesta guia em cerca de 1% - começa a pausar todos os tipos de temporizadores, execução de JS, etc. Essa é uma das primeiras etapas no futuro brilhante.



Após algum tempo no navegador, você terá uma situação em que as guias de segundo plano param de funcionar. Este é o futuro brilhante de que estou falando. De acordo com os planos do Chromium, que eles expressaram no último BlinkOn, em 2020 eles planejam fazê-lo: deixe a guia carregar e, se estiver em segundo plano, não fará nada. Você sempre precisa estar preparado para isso.



No Yandex.Browser, também resolvemos esse problema, mas o resolvemos menos categoricamente e não quebramos a Web inteira. Criamos um modo de economia de energia que desativa a decodificação no processador e deixa apenas a decodificação na placa de vídeo, além de diminuir o FPS e desativar algumas animações que não são necessárias no momento e em vez das quais o usuário deve economizar bateria. Isso nos deu cerca de uma hora de duração extra da bateria. Qualquer um pode verificar, iXBT, por exemplo, verificado .



Acho que alguns dirão: Kostya, você "quebrou" a Web, ajudou alguns usuários, mas não apresentou nada mais inteligente. Adicione hardcore! Como os navegadores desenham páginas?



O conceito de camadas, em poucas palavras, é quando o navegador tenta dividir a página em camadas e desenhá-las separadamente. Isso é feito para que algumas animações sejam executadas e não force o redesenho do que é estático. O navegador faz isso em diferentes heurísticas. Por exemplo - tentando selecionar um elemento de vídeo em uma camada separada, que, obviamente, rapidamente e com frequência redesenha. E se for mostrado em algum lugar, não será necessário redesenhar tudo.

Além disso, cada camada é dividida em tais blocos - retângulos de 256 por 256. No inspetor, você pode ver algo assim. Há um quadro que é dividido em um monte de peças.





O que é e por que é necessário? Primeiro de tudo, para priorizar a renderização. Se temos uma salsicha enorme, na qual todos desenhamos, por que precisamos desenhar tudo isso se agora o usuário vê apenas o que tem agora no ViewPort?



Usando essa abordagem, primeiro desenhamos apenas o que o usuário vê agora no ViewPort, depois um lado a lado e depois na direção do pergaminho. Se o usuário rolar para baixo - puxe para baixo, se for para cima. Todo o resto será desenhado apenas se tivermos uma cota para esses blocos e pudermos desenhá-los, após o que o usuário os verá. Ou talvez nunca.



Também ajuda muito com deficiências. Suponha que um usuário abra uma página, selecione uma peça e não seja necessário redesenhar tudo. Podemos deixar a maior parte da renderização anterior. Vamos redesenhar seis peças aqui, e tudo ficará bem.



Apenas nesse nível, várias otimizações muito bem-sucedidas foram feitas. Por exemplo, o Chromium fez essa otimização por volta de 2017.



Se temos apenas uma renderização pequena, fazemos apenas isso. Em seguida, o cursor pisca e redesenhamos apenas a área do cursor, mas não a área inteira desse bloco. Economizamos muito a CPU para não redesenhar tudo.



Também ajuda a economizar memória. Qual é o problema aqui? Retângulos brancos inteiros. Imagine que seria uma textura de 256 por 256, quatro bytes por pixel. Embora pareça que essa área possa ser codificada com apenas cinco números: coordenadas, largura, altura e cor.



No Chromium, a otimização de áreas monocromáticas foi realizada. Se o navegador entender que não há renderização nesse retângulo, que é completamente monocromático, não é transparente e atende a outras condições, basta dizer à placa de vídeo - desenhe um retângulo branco, não selecione toda a textura.

O que mais pode ser otimizado aqui? Se você olhar para os blocos restantes - eles têm um pouco de conteúdo e uma enorme área de cor branca. No Yandex.Browser, pensamos sobre isso e criamos um mecanismo que chamamos de mosaico adaptável.



Há um pequeno retângulo, um azulejo. Há pouco conteúdo no meio do bloco. Nós o selecionamos e - somente abaixo dele - a textura. Todo o resto também é dividido em várias áreas, das quais estamos falando sobre a placa de vídeo: basta desenhar em branco aqui deste tamanho.



A página também começa a salvar tudo o que está destacado em vermelho. Em páginas mais complexas, é algo parecido com isto.



É importante entender que ainda existem várias camadas e cada camada é desenhada assim. Em cada camada, você pode economizar uma certa quantidade de memória. Essa abordagem nos permitiu economizar cerca de 40% da memória de vídeo em média para todos os usuários.

Mais hardcore! Eles economizaram um pouco de memória aqui, eles "quebraram" a web - por que não "quebram" a web ainda mais?

No Chromium, há algo como uma política: se o usuário não usar as guias em segundo plano, se as tiver deixado, ele não precisará delas. Se agora estamos sem memória e o navegador está desativado, vamos pegar a guia mais antiga que o usuário não usa há muito tempo e matá-la. Ela permanecerá na interface, mas o processo não estará mais lá, todas as JS morrerão. Tudo bem ou não? É estranho fazer essa pergunta na parte do front-end e esperar outra resposta que não seja "O que você está fazendo?"



Então não foi muito. Aqui estão os verdadeiros comentários do blog Chromium: você quebrou todos os meus aplicativos para mim, houve algum tipo de jogo - e, pule, ele não tem nenhum estado. É importante entender que o manipulador de descarga não funcionou, como se tivéssemos simplesmente colocado esta guia. O usuário retorna a ele e o recarregamos da rede, como se nada tivesse acontecido.

Então, essa abordagem foi temporariamente abandonada e surgiu com uma idéia séria mais ponderada. Eles a chamaram de descartada.



Qual é o objetivo? Este é o mesmo assassinato de abas, apenas controlado. Ele é chamado de palavra inteligente Page Lifecycle API . Se você possui uma guia e o usuário não a vê há muito tempo, pode entrar no estado congelado. O navegador diz durante o evento: vou congelar você agora. Após o processamento do evento, nada será executado. Faça o que você precisa, prepare-se.

Em seguida, ele pode sair do estado congelado através do evento resume, supostamente nada aconteceu. Ou, se o navegador realmente precisar liberar memória no momento, ele simplesmente pega e mata. Mas se o usuário voltar a essa guia, recarregaremos e definiremos o campo foi descartado para o documento.



No momento, você já pode se inscrever nesses eventos e capturá-los, de alguma forma processá-los. Se a guia estiver realmente morta, você pode verificar o campo foi descartado. Isso significa que você foi restaurado após um descarte. Você pode restaurar o estado anterior.



Nós da Yandex.Browser pensamos há alguns anos: por que não usar uma abordagem cardinal complexa. Eles o chamavam de hibernação.



Qual é o objetivo? Existem várias guias, algum tipo de JS está sendo executado, algum tipo de estado. Um processo separado é criado para cada guia: aqui um vídeo pode ser reproduzido, aqui você deixa algo no formulário. O Hibernate chega - e não há processos. Todos nós os pegamos. Mas se agora voltarmos a essas guias, o processo voltará e todo o estado estará em vigor, o vídeo continuará sendo reproduzido no momento certo, todo o texto nos campos permanecerá em vigor.



O que fizemos? Três coisas mais importantes vivem dentro de cada renderizador: V8, que executa todo o JS, Blink, que armazena todo o DOM e algum tipo de ligação do navegador, que ajuda a conectar tudo, com guias e tudo mais.



Por exemplo, considere uma amostra. Aqui, esperamos que o onload aconteça e adicionamos um novo elemento div à árvore DOM. Para o navegador, é algo parecido com isto.



Naturalmente, existe uma árvore DOM, ela possui alguns campos, objetos associados e existe essa entidade.



Na V8, o estado de cada nó é armazenado e esses nós são associados a objetos intermitentes por meio de uma camada de ligantes. O que fizemos? Pegamos o serializador da V8, serializamos todo o estado da V8, encontramos todos os objetos relacionados no Blink, escrevemos mais serializadores que salvam toda a árvore do DOM, serializam e depois gravam no disco, compactados e criptografados. E ensinamos o navegador a se recuperar de um instantâneo como esse. Ou seja, quando o usuário acessa essa guia, nós a expandimos, descriptografamos e mostramos ao usuário, restauramos completamente. ( Um post separado sobre o Hibernate - aprox.)

No momento, o Hibernate é publicado para todos os estábulos e permite que cada usuário salve uma média de uma ou duas guias. Ou seja, ele tem em média uma guia sempre salva, ou talvez duas. Isso economiza memória para usuários que têm mais de 10 guias - como fazemos com você, mas não somos representativos.

Eu contei como o navegador está tentando ajudar, mas agora cada um de vocês pode fazer algo para acelerar o site e melhorar seu desempenho. Você pode vir e fazer o certo hoje.

Primeiro você precisa entender se há problemas de memória.



Existem certos sintomas: o site começa a se degradar ou o desbotamento aparece. Geralmente, isso significa que o coletor de lixo é acionado, o mundo inteiro está congelado e nada está sendo desenhado. Ou que o site simplesmente diminui constantemente - isso também acontece.

Você precisa entender se há algum problema. Nós olhamos o que acontece com a memória JS.



Se ele pula para frente e para trás ou cresce continuamente, esse não é um bom sintoma. Ou crescendo continuamente.



Nesse caso, você sempre pode tirar um instantâneo através do DevTools. Alguém já se incomodou com vazamentos em JS? Se alguém não souber, em JS é bem possível fazer um vazamento.



Por exemplo, você tem uma variável global na qual adiciona nós que não são inseridos na árvore. Você os esquece e, em seguida, eles comem centenas de kilobytes ou até megabytes.



A rigor, não está claro o que fazer com esses nós desanexados. Você pode puxá-los para fora da árvore e depois inseri-los de volta. Porque geralmente o estado da imagem ou algo mais permanece com eles. Assim, você pode encontrá-los e tratar o problema.



Você também pode observar a alocação de memória na linha do tempo. Se você tem uma descarga sistemática e nunca limpa, isso também é um mau sinal, acho que todo mundo entende isso.



E você pode brincar com as camadas na sua guia. O navegador geralmente trabalha com heurísticas a esse respeito - tenta separar objetos que considera frequentemente alterados em camadas separadas. Mas, às vezes, não funciona muito bem, e você tem muitas camadas que consomem muita memória. Você sempre pode ver se tem uma situação dessas, eliminar algumas camadas e descobrir quanta memória essa camada ocupa.



Outra camada de problemas é o desempenho. setTimeout é uma má ideia para animação. Não funciona da maneira que o navegador deseja renderizar a página. Pode não funcionar em fase: o navegador solicitou um quadro, mas ainda não há animação, porque setTimeout não funcionou. Ou pode funcionar mesmo quando você não precisa desenhar nada. O usuário saiu, ainda resta algum trabalho em segundo plano, mas trabalharemos com ele por meio de setTimeout.

Aqui está a abordagem correta que seu retorno de chamada chamará apenas quando o navegador precisar desenhar esta página.



Também quero lembrá-lo de que, se o navegador deseja desenhar 60 quadros por segundo, significa que para cada quadro ele precisará de cerca de 16 ms.



E se você ocupar o fluxo principal por mais de 16 ms, terá um salto de quadro garantido, o que é bastante desagradável para o usuário: o site começa a tremer. Portanto, todo o trabalho duro é inserido corretamente nos threads de segundo plano, os quais você simplesmente retorna o resultado.



Ou outra abordagem é usar microtasking. Crie uma fila de tarefas, processe-a após algum tempo, até que a cota termine e deixe o navegador desenhar a página com calma.



Naturalmente, você pode adicionar novas camadas. O navegador trabalha com heurísticas e tenta selecionar a camada onde considerar necessário. Mas se você sabe que agora esse elemento será animado, é melhor informar explicitamente ao navegador que você precisa selecionar esse elemento em uma camada separada. Então será desenhado com mais eficiência.



A última abordagem interessante - reclamar. Se você tiver problemas, é sempre útil vir e conversar. Talvez esse problema possa ser facilmente tratado, e nós nos ajudaremos.

Um pouco de fundo. Este recurso está sendo elaborado atualmente. Temos uma equipe de páginas de desenvolvimento de mecanismos de pesquisa. Eles têm uma métrica como a hora de renderizar o primeiro snippet. Se o usuário visualizar anteriormente o primeiro link no qual você precisa clicar e o que provavelmente lhe dá a resposta correta, é muito melhor desenhá-lo o mais rápido possível. Eles vieram e perguntaram se era possível acelerar de alguma forma dessa vez, porque todos haviam se espremido.

Investigamos, realizamos testes de desempenho, fizemos um protótipo, e podemos fazer isso se o site nos indicar o que desenhar primeiro. Testamos e vemos que isso melhora nosso desempenho. Agora estamos testando isso em produção para um pequeno público. Nós olhamos o que resultará disso. Fique atento.

O mundo não pára. O usuário começa a querer cada vez mais, os navegadores estão mudando, a web está mudando. Isso é normal. Tentamos não apenas criar recursos de supermercado, mas também ajudar o usuário de todas as maneiras possíveis a obter a melhor experiência em termos de consumo de conteúdo. E você sempre precisa estar preparado para alterações em qualquer navegador popular. Naturalmente, se houver problemas, venha, estamos prontos para discutir e ajudar um ao outro.

Aqui reuni vários links úteis:

Source: https://habr.com/ru/post/pt442456/


All Articles