In collaboration with cross-industry browser vendors under the Speedometer governance policy, the Microsoft Edge team is very excited to have contributed to the recently released version 3.0 of Speedometer, as announced on browserbench.org.

First released in 2014, Speedometer is a popular browser benchmark and a key driver of recent browser performance improvements. Version 3.0 has several improvements to the test runner and adds many new tests over version 2.1. An important goal was to capture common, present-day challenges faced by developers by incorporating new variants, frameworks, and modern usage scenarios to the Speedometer tests.

The Microsoft Edge Team contributed a new Complex DOM set of tests to meet these goals. Our aim was to represent the scalability challenges faced by modern web applications, and to drive performance improvements for these applications across the ecosystem.

Motivation: The challenge for modern web applications

Over the past few years, the Edge team collaborated with several Microsoft web application teams to provide expert recommendations for addressing scalability and performance issues. We did this by delving deep into performance profiles, application software stacks, and architecture.

These investigations have consistently shown that the complexity of the DOM and CSS rules is an important driver of end-user perceived latency. Inefficient patterns, often encouraged by popular frameworks, have exacerbated the problem, creating new performance cliffs within modern web applications. The new Complex DOM tests perform DOM mutations in the context of a large DOM tree, which trigger a cascade of costly CSS selector matching operations and style recalculations.

To facilitate the real-world representation of the Complex DOM tests, the Edge team took a data-driven approach to collect more data on DOM sizes and CSS selectors.

DOM sizes – How large is large?

The table below lists some data on DOM tree sizes and their maximum depth in familiar web pages and web application interfaces. We also got inputs on DOM sizes from Adobe in some of their web applications, recognizing the need to capture the performance challenges in the presence of large DOM trees. This gave us a good idea of the DOM tree sizes that would surface rendering performance issues in a benchmark test.

Webpage DOM size Max depth
amazon.com 3746 22
youtube.com 9082 30
Google results page 3458 48
reddit.com 4460 21
MDN Promises article 4167 17
walmart.com 3382 25
Facebook (after scrolling) 9957 63
Gmail (opened email) 3494 69
HBO Max 5478 19
Airbnb.com (login and open filters) 3388 38
Adobe Photoshop (Quick Actions panel) 6751 ≥ 24
Adobe Photoshop (Text Box new) 6331 ≥ 31
bbc.com/news/world 3898 26
nationalgeographic.com 4111 23
Google docs 3558 15
Google sheets 5730 18
Stack overflow post 2521 19
Microsoft Outlook 2870 32
Microsoft Teams 2542 32
apple.com/apple-vision-pro 2857 18
Twitter 2488 59
timesofindia.indiatimes.com 2789 22

CSS selectors – How complex is complex?

Complex CSS selectors are composed by combining several simple selectors using combinators like the descendant, child, adjacent and the sibling combinators. They cause browsers to perform complex matching operations and costly style recalculations. For more details about CSS selector performance, see The truth about CSS selector performance.

The Edge team collected data on CSS selectors that occur in real-world web applications, pointing us to CSS selectors that challenge application performance. A few representative ones are listed below.

Microsoft Outlook:

  • .r9gx1vl:not(:checked) ~ .fui-Radio__indicator > *
  • .f17xb19f > :not(.fui-CardPreview__logo)
  • .appContainer-459 ::after

Microsoft Teams:

  • .r1no3lux pre[class^='language']>code
  • .fiiet6c span:nth-child(1)
  • .f15hebdy>div>div>div
  • .flp1aja span:not(.giphy-gif) img:not([itemtype*="Emoji"])
  • .f1mj8y37>*:not(.ms-StackItem)

Examples from other Microsoft apps:

  • #root ~ script[src*="cdn-client.medium.com"] ~ div > div:not([role]):not([tabindex]):not([id]):not([class*="jfk"]):not([class*="gtx"])
  • ._1WiHgiOg6qjeGNfJmJm0hu .o365cs-base [class^="ms-Icon--"]::before

While the performance challenges posed by complex CSS selectors are widely observable, they manifest differently when they are used in conjunction with other CSS properties or technologies like Web Components.

Variations of the Complex DOM framework

For technologies like Web Components that support Shadow DOMs (an encapsulation mechanism for isolating parts of a DOM tree from the code effects of one another), performance challenges can come from Constructable stylesheets, CSS variables, and fallbacks.

We customized the Complex DOM framework for the Web Components and LIT based implementations to surface these challenges. We also provided two flavors of the Complex DOM framework, one that nests a sizeable chunk of the DOM tree within a CSS stacking context and another that does not as this impacts the rendering order of HTML elements in a DOM tree, which in turn impacts performance in large web applications.

The Complex DOM test

The Complex DOM test is based on a complex application shell, which embeds the TodoMVC application, as shown in the image below:

The Complex DOM test webpage in a browser window.

The key idea is to perform the simple and seemingly isolated actions of the TodoMVC application, such as adding, checking or deleting items, within the broader context of a large and complex web application. This simulates the scalability challenges observed when end users interact with real-world web applications.

The application shell used in the Complex DOM test is fashioned after a generic productivity application to have a real-world look-and-feel, leveraging Adobe’s Spectrum CSS library in its implementation. It consists of a scrollable sidebar element that embeds a large DOM tree, several complex CSS selectors (around 70), and a realistic ribbon menu – totaling to a little more than 6000 DOM elements.

Representing a large DOM structure

The Edge team collaborated internally with Microsoft’s Tensile team to build the Complex DOM application shell, borrowing heavily from their parametrized DOM tree generator. The Tensile framework is designed with the goal of representing the (realistic) costs incurred during page load, style recalculation, layout, and paint by real-world web applications. Early data indicates that it is an accurate predictor of overall performance for applications with complex CSS and large DOMs.

In a case study shipping the Persona control in Fluent, the Tensile framework reported a 30% performance improvement in scripting and styling across two versions of the Persona control implementation which is reflective of the 24% improvement observed in an actual application using the control.

The ribbon contains about 469 descendent elements, and provides drop-down popover menus, for seven relevant and realistic operations. Two of the sample popovers, reminder and backlog, are shown in the images below:

The reminder popup of the Complex DOM test web page The backlog popup of the Complex DOM test web page

Representing complex CSS selectors

The complex DOM application shell consists of about 70 CSS selectors which are constructed by composing them with up to six simple selectors. Some selectors style todo list elements while others style the popovers in the ribbon. They are designed such that all of them either fully match or partially match a todo list element.

A partial match is when a sub selector matches a DOM element, but the entire selector does not. This ensures that the Complex DOM shell captures the performance issues caused by both matching and partially matching CSS selectors to well represent real-world user interactions in complex stateful situations.

For example, consider the following two selectors:

  • .todo-area .todo-list li .toggle:checked + label
  • .reminder-group .high-priority:hover > :checked ~ label

The first selector matches an element in the todo list whereas the second selector does not because no todo list element has the .high-priority class. However, a todo element would partially match the rightmost portion of the selector, i.e., :checked ~ label.

Try it out!

We are eager for you to check out Speedometer 3.0. We think that the collaboration between cross-industry browser vendors has been very effective in capturing real-world challenges on the web in browser benchmarks like Speedometer.

We look forward to continuing this collaboration and making the web more performant for you all! Feedback, comments, new ideas and suggestions for improvements are very welcome! Please send them to the Speedometer GitHub repository.

– Luis Fernando Pardo Sixtos, Issack John, Sean Monahan, Sulekha Kulkarni, Leo Lee, Alex Russell

Source: Windows Blog