Verbetering van de prestaties van Chromium-toegankelijkheid

Dit bericht is van Chromium-bijdrager Ahmed Elwasefi, waarin hij vertelt hoe hij een bijdrager werd via de Google Summer of Code, en de problemen met de toegankelijkheidsprestaties die hij heeft geïdentificeerd en opgelost.

Toen ik mijn laatste jaar computertechniek aan de Duitse universiteit in Caïro naderde, besloot ik de mogelijkheden te verkennen om bij te dragen aan open source. Ik begon Chromium's lijst met beginnersvriendelijke problemen te verkennen en merkte dat ik vooral aangetrokken werd door toegankelijkheid. Mijn zoektocht naar begeleiding bracht mij bij Aaron Leventhal, wiens expertise en bereidheid om te helpen mij inspireerden om met hem samen te werken aan een project. Deze samenwerking werd mijn Google Summer of Code- ervaring, waar ik werd aangenomen om samen te werken met het Chromium Accessibility Team.

Nadat ik Google Summer of Code met succes had afgerond, bleef ik een onopgelost scrollprobleem aanpakken, gedreven door de wens om de prestaties te verbeteren. Dankzij twee subsidies uit het OpenCollective-programma van Google kon ik aan dit project blijven werken, terwijl ik ook extra taken op me nam die gericht waren op het stroomlijnen van code voor betere prestaties.

In deze blogpost deel ik mijn reis met Chromium de afgelopen anderhalf jaar, waarin de technische verbeteringen worden beschreven die we hebben aangebracht, vooral op het gebied van prestaties.

Hoe toegankelijkheidscode de prestaties in Chrome beïnvloedt

De toegankelijkheidscode van Chrome helpt ondersteunende technologieën zoals schermlezers toegang te krijgen tot internet. Wanneer dit echter is ingeschakeld, kan dit van invloed zijn op de laadtijden, prestaties en levensduur van de batterij. Als deze code niet nodig is, blijft deze code daarom inactief om te voorkomen dat de prestaties vertragen. Ongeveer 5-10% van de gebruikers heeft toegankelijkheidscode ingeschakeld, vaak als gevolg van tools zoals wachtwoordbeheerders en antivirussoftware die API's voor platformtoegankelijkheid gebruiken. Deze tools zijn afhankelijk van deze API's om te communiceren met pagina-inhoud en deze aan te passen, zoals het lokaliseren van wachtwoordvelden voor wachtwoordbeheerders en formulierinvullers.

De totale verslechtering van de kernstatistieken is nog niet bekend, maar een recent experiment genaamd Auto Disable Accessibility (de toegankelijkheid uitschakelen wanneer deze niet wordt gebruikt) laat zien dat deze vrij hoog is. Het probleem doet zich voor vanwege de enorme hoeveelheden berekeningen en communicatie in twee hoofdgebieden van de toegankelijkheidscodebasis van Chrome: de renderer en de browser. De renderer verzamelt informatie over webinhoud en wijzigingen in de inhoud, en berekent toegankelijkheidseigenschappen voor een boom met knooppunten. Alle vervuilde knooppunten worden vervolgens geserialiseerd en via een pijp naar de hoofd-UI-thread van het browserproces gestuurd. Deze thread ontvangt en deserialiseert deze informatie in de identieke boom van knooppunten en converteert deze vervolgens naar een geschikte vorm voor ondersteunende technologieën van derden, zoals schermlezers.

Verbeteringen in de toegankelijkheid van Chromium

De volgende projecten zijn voltooid tijdens Summer of Code en daarna, gefinancierd door het Google OpenCollective-programma.

Verbetering van de cache

In Chrome is er een speciale gegevensstructuur, de toegankelijkheidsboom genaamd, die de DOM-boom weerspiegelt. Het wordt gebruikt om ondersteunende technologieën toegang te geven tot webinhoud. Soms, wanneer een apparaat informatie uit deze boom nodig heeft, is het mogelijk niet gereed en moet de browser deze verzoeken voor later plannen.

Voorheen werd deze planning afgehandeld met behulp van een methode genaamd sluitingen, waarbij terugbelgesprekken in een wachtrij werden geplaatst. Deze aanpak zorgde voor extra werk vanwege de manier waarop sluitingen worden verwerkt.

Om dit te verbeteren zijn we overgestapt op een systeem met enums. Aan elke taak wordt een specifieke enumwaarde toegewezen, en zodra de toegankelijkheidsboom gereed is, wordt de juiste methode voor die taak aangeroepen. Deze wijziging maakte de code begrijpelijker en verbeterde de prestaties met meer dan 20% .

Runtime prestatietestgrafieken.
Een grafiek van de looptijden van verschillende prestatietests, waarbij er voor alle tests een zichtbare daling van ongeveer 20% is.

Problemen met scrollprestaties vinden en oplossen

Vervolgens heb ik onderzocht hoe de prestaties verbeteren als we de serialisatie van de selectiekaders uitschakelen. Begrenzingsvakken zijn de posities en afmetingen van elementen op een webpagina, inclusief details zoals breedte, hoogte en hun positie ten opzichte van het bovenliggende element.

Om dit te testen hebben we tijdelijk de code verwijderd die de selectiekaders verwerkt en prestatietests uitgevoerd om de impact te zien. Eén test, focus-links.html, liet een enorme verbetering zien van ongeveer 1618%. Deze ontdekking werd de basis voor verder werk.

Onderzoek naar de langzame test

Ik begon te onderzoeken waarom die specifieke test traag was met selectiekaders. Het enige dat de test deed, was de focus op verschillende links achter elkaar leggen. Daarom moet het belangrijkste probleem het focussen op elementen zijn, of het scrollen dat gebeurde met focusactie. Om dit te testen heb ik {preventScroll: true} toegevoegd aan de focus() -aanroep in de prestatietest, waardoor het scrollen werd gestopt.

Als scrollen uitgeschakeld was, daalde de testtijd tot 1,2 milliseconden wanneer berekeningen voor het selectiekader actief waren. Hieruit bleek dat scrollen het echte probleem was.

De testresultaten met scrollen uitgeschakeld.
De testlooptijd van Focus-links daalt van 20 ms naar 1,1 ms wanneer scrollen is uitgeschakeld of de serialisatie van de selectiekaders is verwijderd.

Ik heb een nieuwe test gemaakt met de naam scroll-in-page.html om de focus-links- test te repliceren, maar in plaats van focus te gebruiken, scrollt deze door elementen met scrollIntoView() . Ik heb zowel soepel als direct scrollen getest, met en zonder berekeningen van het selectiekader.

De testresultaten voor de nieuwe test.
De tijd die nodig is om scrollen te verwerken bij direct scrollen is 65 ms, terwijl soepel scrollen 123 ms duurt.

Uit de resultaten bleek dat het proces bij direct scrollen en selectiekaders ongeveer 66 ms duurde. Vloeiend scrollen was zelfs nog langzamer met ongeveer 124 ms. Toen we de selectiekaders uitschakelden, kostte het helemaal geen tijd omdat er geen gebeurtenissen werden geactiveerd.

We kenden het geval, maar waarom gebeurde het?

We hadden nu geleerd dat scrollen de bron is van veel traagheid in de toegankelijkheidsserialisatie, maar we moesten nog steeds uitzoeken waarom. Om dit te analyseren werden twee tools genaamd perf en pprof gebruikt om het werk dat in het browserproces werd gedaan op te splitsen. Deze tools worden in C++ vaak gebruikt voor profilering. De volgende grafieken tonen een fragment van het interessante deel.

Grafiek van geprofileerde scrolltests.
Een grafiek gegenereerd op basis van profilering van scrolltests. Laat zien dat de tijd vooral wordt besteed aan het aanroepen van een functie genaamd Unserialize en een andere genaamd IsChildOfLeaf.

Na onderzoek kwamen we erachter dat het probleem niet de deserialisatiecode zelf was, maar de frequentie van aanroepen ernaar. Om dit te begrijpen, moeten we kijken hoe toegankelijkheidsupdates werken in Chromium. Updates worden niet afzonderlijk verzonden; in plaats daarvan is er een centrale locatie genaamd AXObjectCache waarin alle eigenschappen worden opgeslagen. Wanneer een knooppunt verandert, informeren verschillende methoden de cache om dat knooppunt als vuil te markeren voor latere serialisatie. Vervolgens worden alle eigenschappen van vuile notities, inclusief ongewijzigde eigenschappen, geserialiseerd en naar de browser verzonden. Hoewel dit ontwerp de code vereenvoudigt en de complexiteit vermindert door één enkel updatepad te hebben, wordt het traag als er snelle 'markeer als vies'-gebeurtenissen plaatsvinden, zoals die van scrollen. Het enige dat verandert zijn de scrollX en scrollY -waarden; toch serialiseren we de rest van de eigendommen er elke keer mee. Het aantal updates bereikte hier meer dan twintig keer per seconde!

De serialisatie van de omsluitende box lost dit probleem op door gebruik te maken van een sneller serialisatiepad dat alleen de details van de omsluitende box verzendt, waardoor snelle updates mogelijk zijn zonder andere eigenschappen te beïnvloeden. Deze methode verwerkt efficiënt de wijzigingen in het selectiekader.

Scroll-fix

De oplossing was duidelijk: neem de huidige scroll-offsets op met de serialisatie van de selectiekaders. Dit zorgt ervoor dat scrollende updates via het snelle pad worden verwerkt, waardoor de prestaties zonder onnodige vertragingen worden verbeterd. Door scroll-offsets te verpakken met selectiekadergegevens optimaliseren we het proces voor soepelere en efficiëntere updates, waardoor een minder vervelende ervaring ontstaat voor gebruikers met ingeschakelde toegankelijkheid. De verbetering na de implementatie van de oplossing bedraagt ​​tot 825% in scrolltests.

Codevereenvoudigingen

In deze periode heb ik mij gericht op codekwaliteit als onderdeel van een project genaamd Onion Soup, dat de code vereenvoudigt door onnodige verspreiding van code over lagen te verminderen of te verwijderen.

Het eerste project had tot doel de manier te stroomlijnen waarop toegankelijkheidsgegevens worden geserialiseerd van de renderer naar de browser. Voorheen moesten de gegevens door een extra laag voordat ze hun bestemming bereikten, wat voor onnodige complexiteit zorgde. We hebben dit proces vereenvoudigd door toe te staan ​​dat de gegevens rechtstreeks worden verzonden, zonder tussenpersoon.

Daarnaast hebben we enkele verouderde gebeurtenissen geïdentificeerd en verwijderd die onnodig werk in het systeem veroorzaakten, zoals een gebeurtenis die werd geactiveerd wanneer een lay-out voltooid was. Deze hebben we vervangen door een efficiëntere oplossing.

Er zijn ook nog andere kleine verbeteringen aangebracht. Helaas zijn hiervoor geen prestatieverbeteringen geregistreerd, maar we zijn er trots op dat de code een stuk duidelijker en zelfdocumenterend is dan hij was. Dit helpt enorm om de weg vrij te maken voor toekomstige prestatieverbeteringen. Je kunt de daadwerkelijke wijzigingen bekijken in mijn gerritprofiel .

Conclusie

De samenwerking met het Chromium Accessibility Team was een lonende reis. Door het aanpakken van verschillende uitdagingen, van het optimaliseren van de scrollprestaties tot het vereenvoudigen van de codebase, heb ik een dieper inzicht gekregen in de ontwikkeling van een dergelijk grootschalig project en heb ik belangrijke tools voor profilering geleerd. Bovendien heb ik geleerd hoe cruciaal toegankelijkheid is voor het creëren van een web dat inclusief is voor iedereen. De verbeteringen die we hebben aangebracht verbeteren niet alleen de gebruikerservaring voor degenen die afhankelijk zijn van ondersteunende technologieën, maar dragen ook bij aan de algehele prestaties en efficiëntie van de browser.

De prestatieresultaten waren indrukwekkend. De overstap naar het gebruik van enums voor het plannen van taken verbeterde de prestaties bijvoorbeeld met ruim 20% . Bovendien resulteerde onze scroll-oplossing in een vermindering van maar liefst 825% in scrolltests. Veranderingen in codevereenvoudigingen hebben de code niet alleen duidelijker en beter onderhoudbaar gemaakt, maar hebben ook de weg vrijgemaakt voor toekomstige verbeteringen.

Ik wil graag mijn dank betuigen aan Stefan Zager, Chris Harrelson en Mason Freed voor hun steun en begeleiding gedurende het hele jaar, en vooral aan Aaron Leventhal, zonder wie deze kans niet mogelijk zou zijn geweest. Ik wil ook Tab Atkins-Bittner en het GSoC-team bedanken voor hun steun.

Voor degenen die willen bijdragen aan een betekenisvol project en hun vaardigheden willen ontwikkelen, raad ik ten zeerste aan om betrokken te raken bij Chromium. Het is een geweldige manier om te leren, en programma's als Google Summer of Code bieden een uitstekend startpunt voor je reis.