Questo post è di Ahmed Elwasefi, collaboratore di Chromium, che racconta come è diventato un collaboratore tramite il Google Summer of Code e i problemi di prestazioni di accessibilità che ha identificato e risolto.
Mentre mi avvicinavo al mio ultimo anno di ingegneria informatica all'Università di Germania in Cairo, ho deciso di esplorare le opportunità per contribuire all'open source. Ho iniziato a esplorare l'elenco dei problemi di Chromium adatti ai principianti e mi sono sentito particolarmente attratto dall'accessibilità. La mia ricerca di indicazioni mi ha portato ad Aaron Leventhal, la cui competenza e disponibilità ad aiutare mi hanno spinto a collaborare con lui per un progetto. Questa collaborazione è diventata la mia esperienza di Google Summer of Code, in cui mi è stato chiesto di collaborare con il team di accessibilità di Chromium.
Dopo aver completato con successo Google Summer of Code, ho continuato a risolvere un problema di scorrimento irrisolto, spinto dal desiderio di migliorare le prestazioni. Grazie a due sovvenzioni del programma OpenCollective di Google, ho potuto continuare a lavorare a questo progetto e, al contempo, assumere altre attività incentrate sulla semplificazione del codice per migliorare le prestazioni.
Questo post del blog racconta il mio percorso con Chromium nell'ultimo anno e mezzo, descrivendo nel dettaglio i miglioramenti tecnici che abbiamo apportato, in particolare nell'area delle prestazioni.
In che modo il codice di accessibilità influisce sulle prestazioni in Chrome
Il codice di accessibilità di Chrome consente alle tecnologie per la disabilità, come gli screen reader, di accedere al web. Tuttavia, se è attiva, può influire sui tempi di caricamento, sulle prestazioni e sulla durata della batteria. Pertanto, se non necessario, questo codice rimane inattivo per evitare di rallentare le prestazioni. Circa il 5-10% degli utenti ha attivato il codice di accessibilità, spesso a causa di strumenti come gestori delle password e software antivirus che utilizzano API di accessibilità della piattaforma. Questi strumenti si basano su queste API per interagire con e modificare i contenuti delle pagine, ad esempio per individuare i campi delle password per i gestori delle password e i compilatori di moduli.
Il degrado totale delle metriche principali non è ancora noto, ma un esperimento recente chiamato Disattivazione automatica dell'accessibilità (disattiva l'accessibilità quando non è in uso) mostra che è piuttosto elevato. Il problema si verifica a causa delle enormi quantità di calcoli e comunicazioni in due aree principali del codebase dell'accessibilità di Chrome: il renderer e il browser. Il renderer raccoglie informazioni sui contenuti web e sulle relative modifiche, calcolando le proprietà di accessibilità per un albero di nodi. Eventuali nodi sporchi vengono poi serializzati e inviati tramite una pipeline al thread dell'interfaccia utente principale del processo del browser. Questo thread riceve e deserializza queste informazioni nell'identica struttura ad albero di nodi e infine le converte in un formato adatto per le tecnologie per la disabilità di terze parti, come gli screen reader.
Miglioramenti all'accessibilità di Chromium
I seguenti progetti sono stati completati durante il Summer of Code e successivamente, finanziati dal programma Google OpenCollective.
Miglioramento della cache
In Chrome esiste una struttura di dati speciale chiamata albero di accessibilità che rispecchia l'albero DOM. Viene utilizzato per aiutare le tecnologie per la disabilità ad accedere ai contenuti web. A volte, quando un dispositivo ha bisogno di informazioni da questa struttura, potrebbe non essere pronto, quindi il browser deve pianificare queste richieste per un momento successivo.
In precedenza, questa pianificazione veniva gestita utilizzando un metodo chiamato chiusure, che prevedeva l'inserimento di callback in una coda. Questo approccio ha comportato un lavoro aggiuntivo a causa del modo in cui vengono elaborate le chiusure.
Per migliorare questo aspetto, abbiamo adottato un sistema che utilizza gli enum. A ogni attività viene assegnato un valore enumerato specifico e, una volta che l'albero di accessibilità è pronto, viene chiamato il metodo corretto per l'attività. Questa modifica ha reso il codice più facile da comprendere e ha migliorato le prestazioni di oltre il 20%.
Individuare e risolvere i problemi di prestazioni dello scorrimento
Successivamente, ho esaminato il modo in cui il rendimento migliora quando disattiviamo la serializzazione della delimitazione. Le caselle delimitanti sono le posizioni e le dimensioni degli elementi di una pagina web, inclusi dettagli come larghezza, altezza e posizione rispetto all'elemento principale.
Per testarlo, abbiamo rimosso temporaneamente il codice che gestisce le caselle delimitanti e abbiamo eseguito test sulle prestazioni per valutarne l'impatto. Un test, focus-links.html, ha mostrato un enorme miglioramento di circa il 1618%. Questa scoperta è diventata la base per lavori futuri.
Indagine sul test lento
Ho iniziato a indagare sul motivo per cui questo test specifico era lento con le caselle delimitanti. Il test non ha fatto altro che mettere a fuoco diversi link uno dopo l'altro. Pertanto,
il problema principale deve riguardare lo stato attivo degli elementi o lo scorrimento avvenuto con l'azione di attivazione. Per testarlo, ho aggiunto {preventScroll: true}
alla chiamata focus()
nel test di prestazioni, interrompendo lo scorrimento.
Con lo scorrimento disattivato, il tempo del test è sceso a 1,2 millisecondi quando i calcoli della casella delimitante erano attivi. Ciò ha dimostrato che il problema principale era lo scorrimento.
Ho creato un nuovo test denominato scroll-in-page.html per replicare il test focus-links, ma anziché utilizzare lo stato attivo, scorre gli elementi con scrollIntoView()
. Ho testato lo scorrimento fluido e istantaneo, con e senza calcoli delle caselle delimitanti.
I risultati hanno dimostrato che con lo scorrimento istantaneo e i riquadri di delimitazione, il processo richiede circa 66 ms. Lo scorrimento fluido era ancora più lento, circa 124 ms. Quando abbiamo disattivato i bounding box, non ci è voluto molto tempo perché non sono stati attivati eventi.
Conoscevamo la situazione, ma perché si verificava?
Ora sapevamo che lo scorrimento è la causa di molti rallentamenti nella serializzazione dell'accessibilità, ma dovevamo ancora scoprire perché. Per analizzarlo, sono stati utilizzati due strumenti chiamati perf e pprof per suddividere il lavoro svolto nel processo del browser. Questi strumenti vengono spesso utilizzati in C++ per il profiling. I seguenti grafici mostrano un snippet della parte interessante.
Dall'indagine è emerso che il problema non riguarda il codice di deserializzazione
in sé, ma la frequenza delle chiamate. Per capire questo, dobbiamo esaminare come funzionano gli aggiornamenti di accessibilità in Chromium. Gli aggiornamenti non vengono inviati singolarmente, ma esiste una posizione centrale chiamata AXObjectCache
che memorizza tutte le proprietà. Quando un nodo cambia, vari metodi avvisano la cache di contrassegnare il nodo come sporco per la serializzazione successiva. Successivamente, tutte le proprietà delle note sporche, incluse quelle invariate, vengono serializzate e inviate al browser. Anche se questo design semplifica il codice e riduce la complessità grazie a un unico percorso di aggiornamento, diventa lento quando si verificano eventi di "contrassegna come modificato" rapidi, ad esempio quelli causati dallo scorrimento. L'unica cosa che cambia sono i valori scrollX
e scrollY
; tuttavia, serializziamo sempre il resto delle proprietà con questi valori. La frequenza degli aggiornamenti ha raggiunto più di venti volte al secondo.
La serializzazione della casella delimitante risolve questo problema utilizzando un percorso di serializzazione più rapido che invia solo i dettagli della casella delimitante, consentendo aggiornamenti rapidi senza influire su altre proprietà. Questo metodo gestisce in modo efficiente le modifiche della delimitazione.
Correzione dello scorrimento
La soluzione era chiara: includere gli offset di scorrimento correnti con la serializzazione della bounding box. In questo modo, gli aggiornamenti di scorrimento vengono elaborati tramite il percorso rapido, migliorando le prestazioni senza ritardi inutili. Impacchettando gli offset di scorrimento con i dati delle caselle delimitanti, ottimizziamo il processo per aggiornamenti più fluidi ed efficaci, creando un'esperienza meno discontinua per gli utenti con l'accessibilità attivata. Il miglioramento dopo l'implementazione della correzione è fino all'825% nei test di scorrimento.
Semplificazioni del codice
In questo periodo, mi sono concentrato sulla qualità del codice nell'ambito di un progetto chiamato Onion Soup, che semplifica il codice riducendo o rimuovendo il codice distribuito inutilmente tra i livelli.
Il primo progetto aveva lo scopo di semplificare il modo in cui i dati di accessibilità vengono serializzati dal visualizzatore al browser. In precedenza, i dati dovevano passare attraverso un livello aggiuntivo prima di raggiungere la destinazione, il che aggiungeva complessità non necessaria. Abbiamo semplificato questo procedura consentendo l'invio diretto dei dati, eliminando l'intermediario.
Inoltre, abbiamo identificato e rimosso alcuni eventi obsoleti che causavano un lavoro non necessario nel sistema, ad esempio uno che si attivava al completamento di un layout. Le abbiamo sostituite con una soluzione più efficiente.
Sono stati apportati anche altri piccoli miglioramenti. Purtroppo, non sono stati registrati miglioramenti del rendimento per questi elementi, ma siamo orgogliosi di comunicarti che il codice è molto più chiaro e autodocumentante di prima. Questo è molto utile per preparare il terreno per i miglioramenti futuri del rendimento. Puoi controllare le modifiche effettive nel mio profilo Gerrit.
Conclusione
Collaborare con il team di accessibilità di Chromium è stato un percorso gratificante. Affrontando varie sfide, dall'ottimizzazione del rendimento dello scorrimento alla semplificazione della base di codice, ho acquisito una conoscenza più approfondita dello sviluppo in un progetto di così vasta portata, oltre a imparare strumenti importanti per il profiling. Inoltre, ho imparato quanto sia fondamentale l'accessibilità per creare un web inclusivo per tutti. I miglioramenti apportati non solo migliorano l'esperienza utente per chi si affida alle tecnologie per la disabilità, ma contribuiscono anche alle prestazioni e all'efficienza complessive del browser.
I risultati in termini di rendimento sono stati impressionanti. Ad esempio, il passaggio all'utilizzo di enumerazioni per la pianificazione delle attività ha migliorato le prestazioni di oltre il 20%. Inoltre, la correzione dello scorrimento ha comportato una riduzione fino all'825% nei test di scorrimento. Le modifiche di semplificazione del codice non solo hanno reso il codice più chiaro e manutenibile, ma hanno anche spianato la strada per i miglioramenti futuri.
Vorrei esprimere la mia gratitudine a Stefan Zager, Chris Harrelson e Mason Freed per il loro supporto e la loro guida durante tutto l'anno e in particolare a Aaron Leventhal, senza il quale questa opportunità non sarebbe stata possibile. Vorrei anche ringraziare Tab Atkins-Bittner e il team del GSoC per il loro supporto.
A chi vuole contribuire a un progetto significativo e sviluppare le proprie competenze, consiglio vivamente di collaborare con Chromium. È un ottimo modo per imparare e programmi come Google Summer of Code offrono un ottimo punto di partenza per il tuo percorso.