Esta postagem é do colaborador do Chromium Ahmed Elwasefi, que compartilha como ele se tornou um colaborador no Google Summer of Code e os problemas de desempenho de acessibilidade que ele identificou e corrigiu.
Quando cheguei ao último ano de engenharia da computação na Universidade Alemã em Cairo, decidi aproveitar as oportunidades para contribuir com o código aberto. Comecei a conferir a lista de problemas do Chromium para iniciantes e me interessei particularmente pela acessibilidade. Minha busca por orientação me levou a Aaron Leventhal, que, com sua experiência e vontade de ajudar, me inspirou a fazer uma parceria com ele em um projeto. Essa colaboração se tornou minha experiência no Google Summer of Code, em que fui aceita para trabalhar com a equipe de acessibilidade do Chromium.
Depois de concluir o Google Summer of Code, continuei a resolver um problema de rolagem não resolvido, motivado pelo desejo de melhorar o desempenho. Graças a duas bolsas do programa OpenCollective do Google, pude continuar trabalhando neste projeto e também assumir outras tarefas focadas em simplificar o código para melhorar o desempenho.
Esta postagem do blog compartilha minha jornada com o Chromium nos últimos 18 meses, detalhando as melhorias técnicas que fizemos, principalmente na área de desempenho.
Como o código de acessibilidade afeta o desempenho no Chrome
O código de acessibilidade do Chrome ajuda tecnologias adaptativas, como leitores de tela, a acessar a Web. No entanto, quando ativado, ele pode afetar os tempos de carregamento, o desempenho e a duração da bateria. Portanto, se não for necessário, esse código vai permanecer inativo para não diminuir a performance. Aproximadamente 5 a 10% dos usuários têm o código de acessibilidade ativado, geralmente devido a ferramentas como gerenciadores de senhas e softwares antivírus que usam APIs de acessibilidade da plataforma. Essas ferramentas dependem dessas APIs para interagir e modificar o conteúdo da página, como localizar campos de senha para gerenciadores de senha e preenchimento de formulários.
A degradação total nas métricas principais ainda não é conhecida, mas um experimento recente chamado "Desativar acessibilidade automaticamente" (desativa a acessibilidade quando ela não é usada) mostra que ela é bastante alta. O problema ocorre devido à quantidade enorme de computação e comunicação em duas áreas principais do código-base de acessibilidade do Chrome: o renderizador e o navegador. O renderizador coleta informações sobre conteúdos da Web e mudanças no conteúdo, calculando propriedades de acessibilidade para uma árvore de nós. Todos os nós sujos são serializados e enviados por um pipe para a linha de execução de interface principal do processo do navegador. Essa linha de execução recebe e deserializa essas informações na árvore idêntica de nós e, por fim, as converte em um formato adequado para tecnologias adaptativas de terceiros, como leitores de tela.
Melhorias na acessibilidade do Chromium
Os projetos a seguir foram concluídos durante o Summer of Code e, em seguida, financiados pelo programa Google OpenCollective.
Melhoria do cache
No Chrome, há uma estrutura de dados especial chamada árvore de acessibilidade que reflete a árvore DOM. Ele é usado para ajudar as tecnologias adaptativas a acessar conteúdo da Web. Às vezes, quando um dispositivo precisa de informações dessa árvore, ele pode não estar pronto, então o navegador precisa programar essas solicitações para mais tarde.
Anteriormente, essa programação era processada usando um método chamado fechamentos, que envolvia colocar callbacks em uma fila. Essa abordagem adicionou trabalho extra porque a maneira como as interrupções são processadas.
Para melhorar isso, mudamos para um sistema que usa tipos enumerados. Cada tarefa é atribuída a um valor de tipo enumerado específico e, quando a árvore de acessibilidade está pronta, o método correto para essa tarefa é chamado. Essa mudança tornou o código mais fácil de entender e melhorou o desempenho em mais de 20%.
Como encontrar e corrigir problemas de desempenho de rolagem
Em seguida, analisei como o desempenho melhora quando desativamos a serialização de caixas delimitadoras. As caixas delimitadoras são as posições e os tamanhos dos elementos em uma página da Web, incluindo detalhes como largura, altura e posição em relação ao elemento pai.
Para testar isso, removemos temporariamente o código que processa as caixas de limite e executamos testes de desempenho para conferir o impacto. Um teste, focus-links.html, mostrou uma grande melhoria de cerca de 1618%. Essa descoberta se tornou a base para trabalhos futuros.
Como investigar o teste lento
Comecei a investigar por que esse teste específico estava lento com as caixas delimitadoras. O teste
colocou o foco em vários links um após o outro. Portanto,
o problema principal deve ser o foco em elementos ou a rolagem que
aconteceu com a ação de foco. Para testar isso, adicionei {preventScroll: true}
à
chamada focus()
no teste de performance, interrompendo a rolagem.
Com a rolagem desativada, o tempo de teste caiu para 1,2 milissegundos quando os cálculos de caixa de limite estavam ativos. Isso mostrou que a rolagem era o problema real.
Criei um novo teste chamado scroll-in-page.html para replicar o teste
links de foco, mas, em vez de usar o foco, ele rola pelos elementos
com scrollIntoView()
. Testei a rolagem suave e instantânea, com e
sem cálculos de caixa de limite.
Os resultados mostraram que, com a rolagem instantânea e as caixas delimitadoras, o processo levou cerca de 66 ms. A rolagem suave foi ainda mais lenta, em cerca de 124 ms. Quando desativamos as caixas de limite, não demorou nada, porque nenhum evento foi acionado.
Conhecíamos o caso, mas por que ele estava acontecendo?
Agora sabíamos que a rolagem é a origem de muita lentidão na serialização de acessibilidade, mas ainda precisávamos descobrir o motivo. Para analisar isso, duas ferramentas chamadas perf e pprof foram usadas para detalhar o trabalho realizado no processo do navegador. Essas ferramentas são usadas com frequência em C++ para criar perfis. Os gráficos a seguir mostram um excerto da parte interessante.
Após uma investigação, descobrimos que o problema não era o código de desserialização
em si, mas a frequência de chamadas para ele. Para entender isso, precisamos analisar
como as atualizações de acessibilidade funcionam no Chromium. As atualizações não são enviadas individualmente.
Em vez disso, há um local central chamado AXObjectCache
que armazena todas
as propriedades. Quando um nó muda, vários métodos notificam o cache para marcar esse
nó como sujo para a serialização posterior. Em seguida, todas as propriedades de notas sujas,
incluindo as não alteradas, são serializadas e enviadas ao navegador. Embora
esse design simplifique o código e reduza a complexidade com um único caminho
de atualização, ele fica lento quando há eventos "marcar como sujo" rápidos, como aqueles
da rolagem. A única coisa que muda é o valor de scrollX
e scrollY
.
No entanto, serializamos o restante das propriedades com elas sempre. A
taxa de atualizações aqui chegou a mais de 20 vezes por segundo.
A serialização de caixa delimitadora resolve esse problema usando um caminho de serialização mais rápido que envia apenas os detalhes da caixa delimitadora, permitindo atualizações rápidas sem afetar outras propriedades. Esse método lida de forma eficiente com as mudanças da caixa delimitadora.
Correção de rolagem
A solução era clara: incluir os deslocamentos de rolagem atuais com a serialização de caixa de limite. Isso garante que as atualizações de rolagem sejam processadas pelo caminho rápido, melhorando o desempenho sem atrasos desnecessários. Ao agrupar deslocamentos com dados de caixa delimitadora, otimizamos o processo para atualizações mais suaves e eficientes, criando uma experiência menos instável para usuários com acessibilidade ativada. A melhoria após a implementação da correção é de até 825% em testes de rolagem.
Simplificações de código
Nesse período, me concentrei na qualidade do código como parte de um projeto chamado Sopa de Cebola, que simplifica o código reduzindo ou removendo a propagação desnecessária entre camadas.
O primeiro projeto visa simplificar a forma como os dados de acessibilidade são serializados do renderizador para o navegador. Antes, os dados precisavam passar por uma camada extra antes de chegar ao destino, o que aumentava a complexidade desnecessária. Simplificamos esse processo permitindo que os dados fossem enviados diretamente, eliminando o intermediário.
Além disso, identificamos e removemos alguns eventos desatualizados que estavam causando trabalho desnecessário no sistema, como um que era acionado quando um layout era concluído. Substituímos isso por uma solução mais eficiente.
Também foram feitas outras pequenas melhorias. Infelizmente, as melhorias de performance não foram registradas para esses casos, mas temos orgulho de compartilhar que o código está muito mais claro e autodocumentado. Isso ajuda muito a pavimentar o caminho para melhorias de desempenho futuras. Confira as mudanças reais no meu perfil do Gerrit.
Conclusão
Trabalhar com a equipe de acessibilidade do Chromium foi uma jornada gratificante. Ao enfrentar vários desafios, desde a otimização do desempenho de rolagem até a simplificação da base de código, tive uma compreensão mais profunda do desenvolvimento em um projeto de grande escala e aprendi ferramentas importantes para a criação de perfis. Além disso, aprendi como a acessibilidade é fundamental para criar uma Web inclusiva para todos. As melhorias que fizemos não apenas melhoram a experiência do usuário que depende de tecnologias adaptativas, mas também contribuem para o desempenho e a eficiência geral do navegador.
Os resultados de performance foram impressionantes. Por exemplo, a mudança para o uso de enums para programar tarefas melhorou o desempenho em mais de 20%. Além disso, nossa correção de rolagem resultou em uma redução de até 825% nos testes de rolagem. As mudanças de simplificação do código não apenas tornaram o código mais claro e fácil de manter, mas também abriram caminho para melhorias futuras.
Gostaria de expressar minha gratidão a Stefan Zager, Chris Harrelson e Mason Freed pelo apoio e orientação durante o ano, e especialmente a Aaron Leventhal, sem quem essa oportunidade não seria possível. Também gostaria de agradecer a Tab Atkins-Bittner e a equipe do GSoC pelo suporte.
Para quem quer contribuir com um projeto significativo e desenvolver habilidades, recomendo se envolver com o Chromium. É uma ótima maneira de aprender, e programas como o Google Summer of Code são um excelente ponto de partida para sua jornada.