Page MenuHomePhabricator

Consider disabling data URI embedding in CSSMin for ResourceLoader
Closed, DeclinedPublic


Even on cached page views and repeat views, rendering takes a fair amount of time. @Peter suspects the parsing of data URIs to be a possible bottleneck. Especially on mobile.

This is non-trivial to undo since we rely on these to some extend to avoid flashes of unstyled content (e.g. hover states, dialogs etc.). Normally, when browsers load stylesheets, referenced images are not downloaded until after the selector applies (at which point it's kind of too late). We may be able to work around this by using preloading or prefetching, however.

Let's start by investigating a static copy of our page with and without data URI embedding enabled in a stylesheet and run it through WebPageTest and others to compare rendering.

See also:

"Extreme Web Performance for Mobile Devices", Maximiliano Firtman, at Velocity 2015 Amsterdam

Screen Shot 2015-11-12 at 22.20.00.png (351×628 px, 77 KB)

Event Timeline

Krinkle raised the priority of this task from to Needs Triage.
Krinkle updated the task description. (Show Details)
Krinkle added subscribers: Krinkle, Peter, ori, Catrope.

Change 230286 had a related patch set uploaded (by Krinkle):
Do not embed images in file modules as data URIs

Krinkle renamed this task from CSSMin: Consider removing data URI embedding in stylesheets to Consider disabling data URI embedding in CSSMin for ResourceLoader.Nov 12 2015, 10:40 PM
Krinkle set Security to None.
Krinkle removed a project: Patch-For-Review.
ori triaged this task as Medium priority.Nov 16 2015, 7:56 PM
ori moved this task from Inbox to Backlog: Maintenance on the Performance-Team board.

Raising priority. At the very least, there should be no data URIs in above-the-fold CSS. @Krinkle, could you take this on?

ori raised the priority of this task from Medium to High.Nov 30 2015, 10:38 AM

Change 257887 had a related patch set uploaded (by Krinkle):
[WIP] resourceloader: Disable CSSMin data URI embedding

Change 257888 had a related patch set uploaded (by Krinkle):
[WIP] resourceloader: Disable CSSMin data URI embedding

Change 257888 abandoned by Krinkle:
[WIP] resourceloader: Disable CSSMin data URI embedding

Change 257887 abandoned by Krinkle:
[WIP] resourceloader: Disable CSSMin data URI embedding

@Peter and I did some research today with applied to and comparing the before and after in WebPageTest from various browsers and devices.

We previously felt like the browser (especially on mobile devices) were spending a lot of time parsing, processing, and other synchronously allocating image objects for images found in data URIs in the stylesheet.

However after removing all data URIs and replacing them with regular urls that point to the same resource, we found no notable speed gain. Not on desktop/Chrome and not on a real Android device.

A few observations:

  • Bandwidth gained. The main CSS request went from 12K to 8K (presumably due to omission of embedded data URIs). Naturally some of these images were now requested separately (amount to about 1.5K). That's still a 2,5K win, presumably due to no longer loading images that aren't used and/or from the lack of base64 inflation.
  • loadEnd regressed slightly. There is a longer tail of requests in the waterfall. Despite HTTP/2, the tail is longer because these requests start much later. This is an inevitable problem because browsers (by design) only request images referenced by CSS for style rules that apply to elements on the page, which is only known after said elements are parsed.
  • Content images earlier. Images used in the content (e.g. the first main image of the article) are requested earlier and rendered earlier. Previously it requested the stylesheet first, but now in most cases it requests the images first. They download in parallel as before. But the first non-blank render used to be 52% (article without logo and no image), but is now 73% (image included in first render).

Other WebPageTest runs:

3Tests without any change (Before)
7 (median first view)
12Moto G
14 (fastest first view)
17After removing data URIs
21 (median first view)
22 Mostly the same
27Moto G
29 (fastest first view)
34Saves bandwidth (12K > 8KB for main css request). Even with the extra image requests included (~0.5K), because many weren't used.
35Longer tail of image requests. Requests start relatively late (CSS applies, indirection). Final paint regresses.
36On mobile [confirmation needed] content images are requested and rendered earlier (inside the first paint even, previously first paint was without the infobox image). Saves a reflow as well.
37At the cost of rendering interface icons later.
38Maybe keep @embed for the handful of icons that are on every page view. And consider converting to something like @push (T???).
42 * Mobile doesn't reserve image box width/height in infobox, causing reflow. Fine on desktop.

Desktop: (before)

Screen Shot 2015-12-09 at 18.30.15.png (183×810 px, 91 KB)

Screen Shot 2015-12-09 at 18.30.59.png (916×1 px, 56 KB) (after)

Screen Shot 2015-12-09 at 18.34.28.png (177×806 px, 91 KB)

Screen Shot 2015-12-09 at 18.35.17.png (453×755 px, 36 KB)

  • Before: Text and icons (e.g. lock badge) are in first render. Image and rest in final render.
  • After: Text in first render. Icons, image and rest in final render.

Both cases, first render is at the same point, so we didn't get text earlier. Regression.

Mobile: (before)

Screen Shot 2015-12-09 at 18.32.04.png (247×812 px, 84 KB)

Screen Shot 2015-12-09 at 18.33.10.png (458×718 px, 40 KB) (after)
Screen Shot 2015-12-09 at 18.36.06.png (251×811 px, 82 KB)

Screen Shot 2015-12-09 at 18.36.30.png (434×748 px, 32 KB)

  • Before: Text and icons (e.g. hamburger menu) in first render. Infobox image in final render.
  • After: Text and infobox image in first render. Icons in final render.

Having the image in the first render is nice, but in "after" first render was actually 0.3 seconds later. (from 1.9s to 2.2s). So it actually delayed rendering of text (and extension of the blank canvas) by 300ms. But at the gain of having the image in the first render. But with the side-effect of icons popping in much later (potentially distracting).

Either way, the final render ("settled") was also later still. So a loss in almost all ways, except for the small bandwidth gain in the CSS payload.

Action items:

  • Removing data URIs globally doesn't seem to have measurable positive impact. We should not consider embedding to be an anti-pattern.
  • Saving bandwidth for unused resources is still worth pursuing. We should audit individual uses of @embed and consider removing them from places not commonly used above the fold on first render.
  • Removing from from highly-visible areas (e.g. above the fold icons on every page) would cause a regression both in perceived performance and actual NavTiming metrics. Instead, we should keep @embed in those cases for the time being and consider using HTTP/2 push for those in the future (T120984).

Change 230286 abandoned by Krinkle:
Do not embed images in file modules as data URIs

Moving conversation back to T118514 on Phabricator.

Closing per above conclusion. Opened T121730 for the audit.

@Krinkle I'm wondering if your test cases for WebPageTest were first load results? When encoding in base64, the issue stated in this heatedly discussed article from mid 2013:

Regardless of whether the data URI content exists in a cached CSS or HTML file, the browser must decode the image each time a page renders: the decoding cost is paid repeatedly every time a page is viewed.

First off, it's not clear how appropriate the data from the article is nowadays, mid 2013 is a quite a while back. Also, for SVG icons we don't use base64. Only URL encoding is used due to performance reasons. SVG rendering (URL decoding?) speed might have increased significantly in recent mobile browsers.