RUM isn’t just for page level metrics anymore. Thanks to modern browser updates and new techniques we can collect real user data at the object level, finding slow page components and keeping third parties honest.
In this talk we will show you how to use Resource Timing, User Timing, and other browser tricks to time the most important components in your page. We’ll also share recipes for several of the web’s most popular third parties. This will give you a head start on measuring object level performance on your own site.
17. RESOURCE TIMING AVAILABILITY
• IE >= 10
• Chrome
• Opera >= 16
• Latest Opera Mobile, Chrome for Android, IE Mobile
18. RESOURCE TIMING GETS US
INTERESTING THINGS
• Generate a complete waterfall
https://github.com/andydavies/waterfall
• Calculate a cache-hit-ratio per resource
• Identify problem resources
19. CORS: CROSS-ORIGIN RESOURCE
SHARING
• Cross-domain resources only tell you start & end time
• Timing-Allow-Origin: *
20. LIMITATIONS OF RESOURCE TIMING
• Does not report resources that error out, which is one
of the things we care about
• Doesn’t tell you if a response is a 304 or 200
21. CAVEAT ABOUT TESTING
WINDOW.PERFORMANCE
• On Firefox 31, checking window.performance in an
anonymous iframe throws an exception
• So we tried:
if (“performance” in window) {}
22. CAVEAT ABOUT TESTING
WINDOW.PERFORMANCE
• But jslint complains about that
• So we switched to:
if (window.hasOwnProperty(“performance")) {
// I know right?
}
23. CAVEAT ABOUT TESTING
WINDOW.PERFORMANCE
• Which does not work on Internet Explorer 10+!#
• So we ended up with:
try {
if ("performance" in window && window.performance)
...
}
catch(e) {
// WTF
}
26. MEASURING A SINGLE OBJECT
var url = 'http://www.buddybrewer.com/images/buddy.png';!
var me = performance.getEntriesByName(url)[0];!
var timings = { !
loadTime: me.duration, !
dns: me.domainLookupEnd - me.domainLookupStart, !
tcp: me.connectEnd - me.connectStart, !
waiting: me.responseStart - me.requestStart, !
fetch: me.responseEnd - me.responseStart!
}
27. MEASURING A COLLECTION OF
OBJECTS
var i, first, last, entries = performance.getEntries();!
for (i=0; i<entries.length; i++) {!
if (entries[i].name.indexOf('platform.twitter.com') != -1) {!
if (first === undefined) !
first = entries[i];!
if (last === undefined) !
last = entries[i];!
if (entries[i].startTime < first.startTime) !
first = entries[i];!
if (entries[i].responseEnd > last.responseEnd) !
last = entries[i];!
}!
}!
console.log('Took ' + (last.responseEnd - first.startTime) + ' ms');
28. TIME BY INITIATOR TYPE
function timeByInitiatorType() {!
var type, res = performance.getEntriesByType("resource"), o = {};!
for (var i=0;i<res.length;i++) {!
if (o[res[i].initiatorType]) {!
o[res[i].initiatorType].duration += res[i].duration;!
if (res[i].duration > o[res[i].initiatorType].max) o[res[i].initiatorType].max
= res[i].duration;!
if (res[i].duration < o[res[i].initiatorType].min) o[res[i].initiatorType].min
= res[i].duration;!
o[res[i].initiatorType].resources += 1;!
o[res[i].initiatorType].avg = o[res[i].initiatorType].duration /
o[res[i].initiatorType].resources;!
} else {!
o[res[i].initiatorType] = {"duration": res[i].duration, "resources": 1, "avg":
res[i].duration, "max": res[i].duration, "min": res[i].duration};!
}!
}!
return o;!
}
29. FIND THE SLOWEST RESOURCES ON
THE PAGE
function findSlowResources(ms, num) {!
var res = performance.getEntriesByType("resource"), arr = [], i;!
for (i=0; i<res.length; i++) {!
if (res[i].duration > ms) arr.push(res[i]);!
}!
arr.sort(function(a,b){ return b.duration - a.duration });!
return arr.slice(0, num);!
}
30. FIND POTENTIAL SPOFS
function findPossibleSpofs(ms) {!
var res = performance.getEntriesByType("resource"), spofs = [];!
for (var i=0;i<res.length;i++) {!
var isSpof = true;!
for (var j=0;j<res.length;j++) {!
if (res[i].name != res[j].name && !
(res[j].startTime > res[i].startTime && res[j].startTime < res[i].responseEnd) ||!
(res[j].endTime > res[i].startTime && res[j].endTime < res[i].responseEnd) ||!
(res[j].startTime < res[i].startTime && res[j].endTime > res[i].responseEnd)) {!
isSpof = false;!
}!
}!
if (isSpof && res[i].duration > ms) spofs.push(res[i]);!
}!
return spofs;!
}
This code is just an example, however it has O(n2) complexity, which might be very slow
running in production.
31. FIND SLOW HOSTS
function findPerfByHost() {!
var res = performance.getEntriesByType("resource"), obj={};!
for (var i=0;i<res.length;i++) {!
var start = res[i].name.indexOf("://")+3,!
host = res[i].name.substring(start),!
end = host.indexOf("/");!
host = host.substring(0,end);!
if (obj[host]) {!
obj[host].resources += 1;!
obj[host].duration += res[i].duration;!
if (res[i].duration < obj[host].min) obj[host].min = res[i].duration;!
if (res[i].duration > obj[host].max) obj[host].max = res[i].duration;!
obj[host].avg = obj[host].duration / obj[host].resources;!
}!
else {!
obj[host] = {"duration": res[i].duration, "min": res[i].duration, "max": res[i].duration,
"avg": res[i].duration, "resources": 1};!
}!
}!
return obj;!
}