← back to articles

[m.imgur.com] page load performance · Issue #1 · perfs/audits

Save article ToRead Archive Delete · Log out

3 min read · View original · github.com

:horse_racing: m.imgur.com page load perf :horse_racing:


TL;DR

While server-side rendering is critical to get a fast first meaningful paint on
single page apps, JS can still undermine the user experience advantages of
server-side rendering.

By disabling javascript on m.imgur.com, the time from navigation→ the paint of
the primary image is decreased by more than 50% from around 7 seconds to 3
seconds (on a nexus 5 - 3g connection).


What:

m.imgur.com recently rolled out a new version of the site moving from a backbone
app to a serverside rendered React app.

Per usual I was looking at the latest Nicolas Cage gifs when I noticed that the
page seemed to feel sluggish when first loaded, preventing me from being able to
scroll down to see the comments or even see the complete image for almost 2
seconds. Now I don't know about you but 2 seconds between me and Cage is simply
unacceptable.


Looking at a timeline recording of loading an
image on imgur on my phone (Nexus 5) the problem is made quite clear.

image

Because the page is rendered serverside, the image starts to download right away, and gets progressively rendered and displayed to the user. However, the image appears to stop loading at around 3 seconds, which directly lines up with when a whole bunch of JavaScript starts to execute.

image

The fun thing about all of this JavaScript executing is that it is locking up
the main thread in the browser, thus preventing the image from being painted to
the screen until the JS work is all done. As an experiment I loaded the page
with JavaScript disabled just to see the performance difference and was not
surprised to see the page take well under ½ as long to finish displaying the
complete image with 0 user facing jank.

The Why:

When we look at the first long frame and sort the javascript activity by bottom
up we see that the browser is spending almost 40% of the time in two methods
called o and then two anonymous functions…

image

Looking close at the logic in those methods we see a familiar face:

image

It is the browserify module loading logic, this is the exact same problem that we ran into on
tumblr.com.

The next most relevant bit of logic is all contained in the React mount logic;
there may be some slight optimizations to be had here, but it is far away from
being the biggest offender.

image

The "Fix":

The interesting story around imgur is how by moving to a server side rendered
site, you would think that the performance gains would be immense. However, the
time spent in the javascript bootup phase is causing a negative user experience,
due to the JS execution blocking the main thread, and thus blocking image
rendering. This problem, while almost undetectable on a macbook pro or modern
computer, is painfully obvious when you test the site on a mobile phone on a
non-wifi internet connection. There are several ways to fix this issue, all of
which do not require an overwhelming amount of work.

  1. Defer the JavaScript execution until the image onload event
  2. Use a tool like rollup or Nolan Lawson's new rollupify https://github.com/nolanlawson/rollupify/ to drop the overhead (40% of the JS time) of browserify
  3. Break up any block of script that takes over 100ms. Potentially use a scheduling API like requestIdleCallback. https://twitter.com/igrigorik/status/706890158323269633