Page MenuHomePhabricator

Concept: Reduce CSS styles for embedded icons by shipping the SVGs in one request and the PNGs in another for noscript users only
Closed, DeclinedPublic

Description

Timo and I were discussing the problem of our icon styles. Currently for each icon we ship the following to all users (debug version):

…/w/load.php?modules=ext.echo.badgeicons&only=styles&skin=vector

.oo-ui-icon-bell {
	background-image: url(/w/load.php?modules=ext.echo.badgeicons&image=bell&format=rasterized&lang=en-gb&skin=vector);
	background-image: linear-gradient(transparent, transparent), url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22%3E %3Cpath d=%22M17.5 14V9c0-3-2.3-5-5.5-5S6.5 6 6.5 9v5c0 2 0 3-2 3v1h15v-1c-2 0-2-1-2-3zM12 20H9c0 1 1.6 2 3 2s3-1 3-2h-3z%22/%3E %3C/svg%3E");
}

This has an equivalent problem for each of our two groups of users:

  1. SVG-supporting users (the vast majority) have to download a bunch of CSS for the PNG alternative, which is maybe 25% extra bytes, for no purpose.
  2. Non-SVG users (who are using an older device, and likely care about speed and bandwidth meterage) have to download a bunch of CSS for the embedded SVG, which is maybe 75% extra bytes, for no purpose.

We can't vary the request URL because that would mean changing the HTML stored in Varnish, which would violate the entire point of caching. The "normal" thing to do would be having load.php URLs resolve differently based on user agent capability, but that would shatter the Varnish caches (varying on 100s or more of UA strings).

My suggestion was to try to fix only the first problem, by splitting the styles requests into two – one which has the embedded vector images, the other wrapped in <noscript> which has the (? embedded) raster images, and which would over-write the former.

…/w/load.php?modules=ext.echo.badgeicons&only=styles&skin=vector&images=vector

.oo-ui-icon-bell {
	background-image: linear-gradient(transparent, transparent), url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22%3E %3Cpath d=%22M17.5 14V9c0-3-2.3-5-5.5-5S6.5 6 6.5 9v5c0 2 0 3-2 3v1h15v-1c-2 0-2-1-2-3zM12 20H9c0 1 1.6 2 3 2s3-1 3-2h-3z%22/%3E %3C/svg%3E");
}

…/w/load.php?modules=ext.echo.badgeicons&only=styles&skin=vector&images=raster

.oo-ui-icon-bell {
	background-image: url(/w/load.php?modules=ext.echo.badgeicons&image=bell&format=rasterized&lang=en-gb&skin=vector);

Advantages:

  • Significantly drops bandwidth/client side processing demand for most users.
  • Simple to implement.

Disadvantages:

  • Doesn't fix things for PNG users, but instead makes things slightly worse (extra HTTP request, duplication of non-icon styles into second module that won't be gzipped against each other).
    • Extra HTTP handshake cost is pretty minimal given HTTP2 (which might also solve the gzip issue?).
    • Could make this less costly by having a new kind of RL module, icon-only, which this applies to.

Offered as an idea.

Event Timeline

Restricted Application added a project: Performance-Team. · View Herald TranscriptFeb 13 2018, 11:01 PM
Restricted Application added a subscriber: Aklapper. · View Herald Transcript
Krinkle triaged this task as Medium priority.Feb 16 2018, 8:08 AM
Krinkle moved this task from Inbox to Accepted Enhancement on the MediaWiki-ResourceLoader board.

As co-proposer, I support the idea and think we should give it a try. Though, for the record, we don't usually have a noscript-stylesheet be overridden by regular stylesheet. It seems fine from a quick glance, but we'll have to see if it works as expected. Patch is welcome.

Pinging @matmarex as he might have some thoughts about this.

Volker_E added a subscriber: Volker_E.EditedFeb 21 2018, 5:24 PM

So the affected browsers of the PNG fallback in the SVG hack are IE <= 9, Firefox <= 3.5 and Android 2.

To my knowledge, modern CSS engines are capable to built the CSSOM (incl the styles within <noscript/>) before starting all HTTP requests. Are we sure about the extra HTTP request needed even in browsers like IE 9?
Which brings us to the second point: What can we actually save performance-wise? Is the minimal effect in CSS rendering worth it? And as good as this sounded at first sight, we will just move the fallbacks to a <noscript/> tag, which might have a minimal effect in CSS rendering, but byte-wise down the wire it will even increase things slightly.

I've taken the example and numbers from T184227#3898366 with 8 icons on mobile frontend and proceeded:

before quotes mangling35642 bytes8916 bytes
with quotes mangling (not yet merged)35322 bytes8908 bytes
removed PNG fallback (no noscript, only removal34477 bytes8799 bytes
removed linear-gradient34149 bytes8773 bytes

Also to consider: Currently the SVGs are provided by a CSS hack. We might run into certain side-effects when enabling SVGs to IE 9 (not so much Fx 3.5 as it's 0 or close to 0%) by getting rid of the linear-gradient hack.

Also to consider: Currently the SVGs are provided by a CSS hack. We might run into certain side-effects when enabling SVGs to IE 9 (not so much Fx 3.5 as it's 0 or close to 0%) by getting rid of the linear-gradient hack.

We wouldn't in this manner – by aligning JS and SVG requirements, the SVG stylesheet would be over-ridden for IE9 (soon IE10) and below, as well as Firefox 3.x and Safari 4.x. Unless you're worried that the SVG stylesheet would be so badly interpreted by those old browsers that they would break?

Imarlier moved this task from Inbox to Radar on the Performance-Team board.Feb 26 2018, 9:37 PM
Imarlier edited projects, added Performance-Team (Radar); removed Performance-Team.
Krinkle closed this task as Declined.EditedMar 3 2019, 7:52 PM

In 2010, we decided to embed images because of:

  • how browsers worked at the time and our understanding of how browsers worked,
  • the use of HTTP/1 (relatively high cost for each additional request, limited number of connections that each can only start requesting the next resource after the previous has gotten its complete response),
  • our focus on reducing the total time taken and bandwidth spent for loading a page.

Embedding images allowed the images to act like a super-sprite in terms of compression (saving on bytes transferred) and reduced the number of requests (fewer roundtrips, thus faster page load time). Between then and 2019, some things have changed:

  • we now use HTTP/2, in which parallel requests have fewer disadvantages, less overhead, and there is no longer a concurrency limit after which requests would be stalled. There is still non-zero overhead and saving requests can still benefit. But, its overhead is now smaller, and as such there are now also scenarios in which an extra request would actually save time rather than increase time. It's also important to note that we spent fewer bytes on HTTP in HTTP/2 due to the ability to compress values over multiple requests (HPACK).
  • we still consider bandwidth cost an important factor. But for a given individual and a given quantity of bytes on the network, it is now cheaper than it was ten years ago (citation needed). Hence, while 10 KB may've been a deal-breaker in some trade offs before, a similar trade off might now only be justified for 30 KB. Depending on how many bytes a change saves or adds, and how much it improves other factors (e.g. UX, perceived performance, rendering speed, battery power, etc.), it may now be acceptable to be less optimal on bytes if it means a significant improvement on other factors.
  • several browsers now support HTTP Preload.
  • we now have much better insight into visual rendering in browsers (RUM: Paint Timing. WebPageTest: Visual metrics). And we've decided to make them part of the key performance metrics we monitor.

The way browsers work, and have worked even in 2010, is that a stylesheet blocks the appearance of any content or layout on the page. The flow is as follows:

  1. Browser requests HTML page from server, and
  2. Browser starts to receive HTML from server.
  3. Browser detects sub resources, such as stylesheets, images, and scripts. And requests them. Stylesheets are given a high priority through the HTTP/2 semantics.
  4. Browser continues to receive HTML from the server and parses it, but may not yet render.
  5. Browser starts to receive the stylesheet.
  6. Browser receives the remaining parts of the stylesheet.
  7. Browser may now render the HTML it's parsed so far. (First paint)
  8. Browser continues to receive HTML and will progressively parse/render in chunks until it reached the end of the HTML stream. The same applies to images and scripts.

By embedding icons in stylesheets, no page content, text, or layout may appear until all icons are downloaded. This is in essence a sort of "ultra high priority" hack, analogous to synchronous scripts (and we know how much we don't like synchronous scripts). The use of embedding is therefore imho unacceptable, without exception. No icon can be more important than the layout of the website, and the article text itself.

I'm closing this in favour of T120984. In there, we'll evaluate ways to make sure we will not regress visual completion times for the above-the-fold rendering. For example, by introducing a @preload annotation that would eagerly start parallel HTTP requests for those icons before the browser has finished the HTML/CSS rendering; which we would selectively apply to a subset of icons in CSS that we know render above-the-fold on most view ports.