Beacon performance

February 9th, 2014. Tagged: performance

Beacons are small requests that our apps make to report some information "home", to the server. Beacons are often used to report visitor stats, JS errors, performance metrics.

Beacons often don't return any data back to the client, but some do.

Example use

<div id="app">Awesome app is awesome</div>
 
<script>

// gather data somehow
var data = {
 time: Date.now(),
 pixelRatio: window.devicePixelRatio
};
 
// construct query string
var qs = Object.keys(data).map(function(key){
  return encodeURIComponent(key) + '=' + encodeURIComponent(data[key]);
}).join('&');
 
// send beacon
new Image().src = '/stats.png?' + qs; // "/stats.png?time=1391976334544&pixelRatio=1"

</script>

Measuring the beacon's performance

So now you want to know what's the preformance of the beacon - how long did it take to send the beacon out and get a response back.

Simplest thing - add onload and onerror (you'll see why) handlers:

// reporting beacon perf
var reported = false;
var start = Date.now();
function reportBeaconPerf() {
  if (reported) {
    return; // bye
  }
  reported = true;
  new Image().src = '/perf.png?beacon_took=' + (Date.now() - start);
                 // "/perf.png?beacon_took=34802"
}
 
// send beacon
var i = new Image;
i.onerror = i.onload = reportBeaconPerf;
i.src = '/stats.png?' + qs;

You use onload and onerror just in case you decide to return an empty "204 No Content" response from the stats.png beacon. In this case the response is not a valid image, so some browsers may decide, understandably, to fire an error event.

Need more perf data!

Time from declaring you want to make a beacon request (which may be different from the time the browser actually makes it) to the time you get an event back is cool. But you know what's cooler - getting more (and more accu-rat) data. E.g. DNS resolution, connect time, etc. One lump number doesn't tell you where you should focus to improve performance. You need details.

And details you get, thanks to the resource timing API now present in many-a-user-agent. Resource timing is not the same as navigation timing, but the adoption looks promising (http://caniuse.com/#feat=nav-timing). Plus, you're not going to beacon every single time. And probably perf improvements targeted at one browser will help the others (that haven't heard of resource timing yet) too.

Here's a nice intro to resource timing.

To see the magic in action:

  1. In Chrome, go to http://phpied.com
  2. open the console
  3. type
    > var src = "http://phpied.com/favicon.ico?blah=blargh!";
    > new Image().src = src;
  4. type performance.getEntries()
  5. Inspect the last entry in the array

Alternatively, you can also use getEntriesByName(), since you have the URL, e.g.

performance.getEntriesByName(src)[0];

You'll see something like:

rt

Pretty cool, huh? DNS resolution and all.

All the values are relative to performance.timing.navigationStart, in case you need absolute values.

Same-origin restrictions apply, so if you're beaconning to a different domain, make sure the response adds the Timing-Allow-Origin: * HTTP header, like so:

$ curl -I http://connect.facebook.net/en_US/all.js
HTTP/1.1 200 
Cache-Control: public, max-age=1200
Content-Type: application/x-javascript; charset=utf-8
...
Timing-Allow-Origin: *

Improving beacon perf

Now that you know how to measure the beacon perf, you can look at the data and see where you have room for improvements. Here are some pointers to get you started.

Async.

new Image().src is already async, but whatever the file where this JS code lives should be loaded asynchronously as well.

Tiny response.

The smallest response body you can have is 1x1 GIF. PNG is superior format that yields smaller file sizes, but not when it comes to tiny images. The 1x1 image should be 42 bytes. Anything above this is a waste.

No response.

Switch to a 204 response and make the body 0 bytes.

Respond, then log.

From Philip's response here:

It's better to send the header, and flush the output first, and then do your logging. That way the client isn't waiting for you to do your back end work.

Unless, of course, the response is dependent on back end work. In which case, back to the good old profiling of the craziness you're doing server-side. No network involved here.

Remove HTTP headers.

With the big axe. You may send tiny or 0-size response bodies, but what's the benefit if you send tons of headers? Cookie header being the worst.

Thanks!

Thanks for reading! Now go make your beacons faster!

Also keep an eye on Philip's articles here - http://www.lognormal.com/blog/

And please come back and add to that list above with your discoveries.

Tell your friends about this post: Facebook, Twitter, Google+

7 Responses

  1. […] Stefanov from Facebook shows us how to measure and improve a web beacon’s […]

  2. whoah this weblog is excellent i like studying your articles.
    Keep up the good work! You know, lots of people are looking round for this information, you can
    aid them greatly.

  3. vjhgujhgjhghjjhgjh jhgjhg jh ghg jhg jgjh hj gjh j v vbnbv nbv vnv
    fg
    fgfg
    fg
    fgfgdfgdfgdfg
    df

  4. You put plenty of time into this research. But in case you remove the http header, won’t you expect error from client side browser?

  5. Hi there, I do believe your website could be having web browser compatibility problems.
    When I look at your web site in Safari, it looks fine however,
    when opening in I.E., it has some overlapping issues.
    I merely wanted to provide you with a quick heads up!
    Apart from that, great site!

  6. Link exchange is nothing else except it is simply placing the
    other person’s web site link on your page at proper place and other person will also do similar in support of you.

  7. This is a topic that’s near to my heart… Many thanks!
    Exactly where are your contact details though?

Leave a Reply