We define round trip time as the time taken from the user initiating a resource request to when that resource is completely available for the user to interact with. We limit our measurements only to HTML page type resources.
The round trip time is therefore the time from the user clicking on a link to the page referenced by that link becoming usable by the user. For most cases, this is as good as measuring the time from the previous page's onbeforeunload event firing to the current page's onload event firing. In some cases this may be different, but we let the developer determine those events.
This is how we measure.
attach a function to the window.onbeforeunload
, document.onmouseup
(only
activated for links) and form.onsubmit
(for all forms on the page) events.
Inside the last invocation of this function, we take a time reading (in milliseconds) and store it into a session cookie along with the URL of the current page or the URL of any clicked link or submitted form.
attach a function to the window.onunload
(non-Safari) and window.onpagehide
(Safari)
events.
We take a time reading in this function as well, and store it in the session cookie.
attach a function to the window.onload
event.
Inside this function, we take a time reading (in milliseconds) and call this rt.end
.
If the browser has implemented the WebTiming API, we
pull out navigationStart
(or fetchStart
if navigationStart
is unset). To get around a bug in Firefox 7 and 8, we use unloadEventStart
instead.
If the WebTiming API is not supported, we look for the cookie where we wrote the start time, and if found,
use that. This time is set to rt.tstart
. If we find neither, we abort [1].
If we find a cookie, we check the URL stored in the cookie with the document.referrer
of the current document if it was set via onbeforeunload
or the URL of the current page if
the cookie was set via onmouseup
or onsubmit
. If these two differ, it means that
the user possibly visited a third party page in between the two pages from our site and the measurement
is invalid, so we abort [2].
If we're still going, we pull the time out of the cookie and remove the cookie. We
measure the difference in the two times (rt.end - rt.tstart
) and this is the round trip time
for the page.
If the WebTiming API is supported, we check the value of responseStart
. If not, we use the time of the
onunload
or onpagehide
events stored in the cookie, and use this as a proxy for first byte
time (t_fb_approx
). With this, we can calculate the following entities:
rt.end - rt.tstart
(responseStart || t_fb_approx) - rt.tstart
rt.end - (responseStart || t_fb_approx)
Bandwidth and latency are measured by downloading fixed size images from a server and measuring the time it took to download them. We run it in the following order:
First download a 32 byte gif 10 times serially. This is used to measure latency
We discard the first measurement because that pays the price for the TCP handshake (3 packets) and TCP slow-start (4 more packets). All other image requests take two TCP packets (one for the request and one for the response). This gives us a good idea of how much time it takes to make an HTTP request from the browser to our server.
Once done, we calculate the arithmetic mean, standard deviation and standard error at 95% confidence for the 9 download times that we have. This is the latency number that we beacon back to our server.
Next download images of increasing size until one of the times out
We choose image sizes so that we can narrow down on a bandwidth range as soon as possible. See the code comments in plugins/bw.js for full details.
Image timeouts are set at between 1.2 and 1.5 seconds. If an image times out, we stop downloading larger images, and retry the largest image 4 more times[3]. We then calculate the bandwidth for the largest 3 images that we downloaded. This should result in 7 readings unless the test timed out before that [4]. We calculate the median, standard deviation and standard error from these values and this is the bandwidth that we beacon back to our server.
The latest code and docs is available on github.com/SOASTA/boomerang
BW.nruns
parameter. See
Howto #6 for details on configuring boomerang.