perfs / audits Goto Github PK
View Code? Open in Web Editor NEW:rocket: Performance audits for the web: sites, webapps, JS frameworks and libraries.
License: Creative Commons Zero v1.0 Universal
:rocket: Performance audits for the web: sites, webapps, JS frameworks and libraries.
License: Creative Commons Zero v1.0 Universal
While doing a deep dive on crbug.com/668810 "Message board burns an insane amount of energy", I found what seems to be an oversight in Adobe Edge JS.
Adobe Edge JS is/was(?) a popular framework used for animated ads and the issue was observed in this particular ad. However, I believe that this would be a common issue for any animated ad that uses Adobe Edge.
DevTools timeline
The screenshot below shows the activity of an ad using Adobe Edge well after it reached its last frame and became visually static:
You can see that there is a fair amount of activity which correspond to requestAnimationFrame callbacks.
I step-debug the code and found that the framework does check for when an animation reaches its intended duration to notify "observers" but it fails to terminate the animation loop.
In particular, here is the code that drives the animation loop
k.timerFunc = function() {
c.Timeline.tick();
k.requestAnimationFrame.call(window, k.timerFunc)
};
a.tick = function() {
var a = b.slice(0);
b = [];
for (var c = a.length, d = (new Date).getTime(), g = 0; g < c; g++) {
var n = a[g];
typeof n !== "undefined" && n.call(null, d)
}
};
The b variable appears to represent the animation timeline.
When the ads run its course, b is empty but the timerFunc function itself doesn't check for this end state and as far as I could tell there was nothing in the code that would put an end to the timerFunc animation loop.
Audit from: September
In this audit, we're going to explore and speed-up up the front-end to the NASA open-source projects landing page. We noticed code.nasa.gov was particularly slow on desktop over cable and heavy enough to lock up Chrome on Android on a relatively fast Wifi connection. We observed lots of main-thread lockup with very large chunks of work (some of the heaviest frames taking 2500ms+ when we really want this closer to 16ms):
and the Network waterfall indicated there were 433 requests in flight. That's one busy request party.
There are four primary causes of slow-down in this app:
Let's begin with some low hanging fruit. We noticed the site had a few larger image assets. This was trivial to fix and a pass through ImageOptim saved us 20-80% (333KB overall) on our page weight:
nasa/code-nasa-gov@974b08f
Next up we noticed each repo entry on their landing page had a GitHub fork button embed on it:
Each GitHub button embed loaded via an iframe introduces additional network overhead of a few seconds (2.6/2.9s in a few cases on cable).
Overall these are responsible for over 1500ms+ of activity on the main thread (including time for JS execution) that isn't really offering us that much value:
This PR I landed dropping them in favor of folks clicking through to the repo link at the top of each listing instead: nasa/code-nasa-gov@fc44bee
The impact of this change is we also get to remove 92 additional requests from the current network waterfall:
which is but also nixes 100KB+ from the overall payload.
We head back into the network panel and what do we see. Well, images again. We've optimised them all but there's still this
really large background image that ideally would be <100KB:
Let's fix that by running it through https://compressor.io/ and we'll save 75% on the size of that asset: nasa/code-nasa-gov@ba96415
Looking through the network panel again we've got lots of web fonts. Whaa:
We don't want to mess with the design of this page just yet but it definitely has too many fonts on there. Let's at least make sure we async load them in. If we switched to a Web Font Loader (like this) and used it asynchronously we would avoid blocking the page while loading in Angular and Bootstrap.
<link href='//fonts.googleapis.com/css?family=Inconsolata' rel='stylesheet' type='text/css'>
<link href='//fonts.googleapis.com/css?family=Source+Code+Pro' rel='stylesheet' type='text/css'>
<link href='//fonts.googleapis.com/css?family=Droid+Sans+Mono' rel='stylesheet' type='text/css'>
<link href='//fonts.googleapis.com/css?family=Exo+2:400,600,700' rel='stylesheet' type='text/css'>
<link href='//fonts.googleapis.com/css?family=Special+Elite' rel='stylesheet' type='text/css'>
Downside to this is if we use it asynchronously, the rest of the page might render before the Web Font Loader is loaded and executed, which might intro some FOUC. But for now, we're going to land it.
Next, let's dig in and see what can be done about the Angular 1 perf issues on the app. If we CPU profile it in DevTools we see that $digest cycles are taking over 2 seconds!.
If we wanted to get framework specific here, we could use ng-idle-apply-timing, which tries to run the digest cycle without changing any data and collects the CPU profile. It measures how long dirty checking for each piece of data in our app takes (two-way data-binding, $watch expressions..things that add to the digest cycle time). Looking at this we see that one idle digest cycle takes up a second. Not great.
We can also get some insight into how many watch expressions Angular is evaluating by running ng-count-watchers, which goes through each element's scope, summing the total number of found watchers. In our case it's over 10,000:
This is unnecessary overhead as all of our Github repo entries are being loaded in from a static JSON file (the source for data in this app) and aren't going to change.
We can greatly simplify this by switching from two-way to one-way bindings in our code instead. Sam did this over here: nasa/code-nasa-gov@8b2d7e7 and also bumped us up to the latest version of Angular so the page would benefit from any performance improvements that landed since the page was created.
<div ng-repeat="project in filtered = (catalog | orderBy:'Update_Date':true | filter:search)" class="box" ng-show="project.tagFilter">
<div class="title">
<div class="title1">
<h2>{{:: project.Software}}</h2>
</div>
Our improvements up until this point have been deployed to code.nasa.gov on Thursday 8th Sept. We're going to keep hacking on perf after this.
The current version of the front-end didn't have a build step and there were places where it still used JavaScript to fetch a lot of partials, like the templates for entries, sharing and the license (taking 2s to fetch and 630ms+ to parse/execute on a fast Linux desktop):
The site also doesn't currently concat any of the scripts or stylesheets, contributing to that high network request count. Introducing a simple build step here would allow us to start cutting away at some more low-hanging fruit. I landed a PR that added the following to try ameliorating the issue:
<head>
and switch to asynchronously loading them inThis takes the work that was required here:
and reduces it to:
saving on a few hundred milliseconds on the aggregate time for these two blocks of work:
To be clear, there's still a lot of work being done here. The change is we've switched to a single 319KB bundle for all script rather than 7 network requests for scripts and up to 11 additional requests for HTML partials.
Room for improvement:
We could get more granular here. We could only use template caching for partials needed for the initial view and continue XHRing in partials for Guide and Share.
After reviewing the bottlenecks getting Angular 1 fast enough on mobile devices, we decided to completely rewrite the front-end using the PRPL pattern with Polymer (it made doing the rewrite using code-splitting techniques super easy and enabled us to ship in under a week). We'll be sharing more details on the implementation in the near future.
Tweet audit 🐐 https://twitter.com/samccone/status/771786445015035904
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).
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.
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.
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.
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…
Looking close at the logic in those methods we see a familiar face:
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.
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.
This is a request for an audit of a strange issue I have been encountering lately. With the new mobile website (m.reddit.com) when I open a post and within a second hit my backbutton on my Android phone, there is a delay of a couple of seconds. It looks to me that the comment loading is causing a bottleneck before the backbutton is triggered, but I am not sure. Interested to hear about your findings or if you encountered this issue at all.
audit by @samccone.
published 9/13/2016
I would love to have a centralized repo for perf audits. I know @paulirish and @samccone have done some that I have bookmarked. Mainly:
Would it be worthwhile to add those to this repo and link to them all from the readme?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.