SlideShare a Scribd company logo
BEYOND PAGE LEVEL METRICS
Buddy Brewer 
@bbrewer 
Philip Tellis 
@bluesmoon
GITHUB 
git clone <clone url> 
https://github.com/lognormal/ 
beyond-page-metrics 
https://github.com/lognormal/ 
boomerang
WHAT DOES A PAGE LOOK LIKE ON 
THE NETWORK?
HOW DO DIFFERENT BROWSERS 
HANDLE PARALLELIZATION?
WHICH PAGE COMPONENTS 
AFFECT PERCEIVED LATENCY?
ARE ANY OF THEM SPOFS? 
• Static JavaScript files, external CSS files 
• Anything that blocks onload if you have scripts that 
run on onload
CAN’T WE GET THIS 
ALREADY? 
WHY DO WE 
NEED RUM?
San Francisco 
London 
Paris 
Gilroy 
Tellisford 
Eze
Fast 
Connections 
Slow 
Connections
Common Browsers 
Uncommon Browsers
≠
PERFORMANCE TIMING 
NAVIGATION TIMING
NAVIGATION TIMING 
AVAI LABI L ITY 
• IE >= 9 
• FF >= 7 
• Chrome >= 6 
• Opera >= 15 
• Latest Android, Blackberry, 
Opera Mobile, Chrome for 
Android, Firefox for 
Android, IE Mobile
NAVIGATION TIMING EXAMPLE 
var loadEventDuration = performance.timing.loadEventEnd - ! 
performance.timing.loadEventStart;
PERFORMANCE TIMELINE 
RESOURCE TIMING
RESOURCE TIMING AVAILABILITY 
• IE >= 10 
• Chrome 
• Opera >= 16 
• Latest Opera Mobile, Chrome for Android, IE Mobile
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
CORS: CROSS-ORIGIN RESOURCE 
SHARING 
• Cross-domain resources only tell you start & end time 
• Timing-Allow-Origin: *
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
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) {}
CAVEAT ABOUT TESTING 
WINDOW.PERFORMANCE 
• But jslint complains about that 
• So we switched to: 
if (window.hasOwnProperty(“performance")) { 
// I know right? 
}
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 
}
MEASURING XHRS 
!! 
function instrumentXHR()! 
{! 
! var proxy_XMLHttpRequest,! 
! orig_XMLHttpRequest = window.XMLHttpRequest,! 
! readyStateMap;! if (!orig_XMLHttpRequest) {! 
! ! // Nothing to instrument! 
! ! return;! 
!! }! 
! readyStateMap = [ "uninitialized", "open", "responseStart", "domInteractive", "responseEnd" ];! !! 
// We could also inherit from window.XMLHttpRequest, but for this implementation,! 
! // we'll use composition! 
! proxy_XMLHttpRequest = function() {! 
!! ! var req, perf = { timing: {}, resource: {} }, orig_open, orig_send;! 
!! ! req = new orig_XMLHttpRequest;! 
! ! orig_open = req.open;! 
!! ! orig_send = req.send;! 
! ! req.open = function(method, url, async) {! 
! ! ! if (async) {! 
! ! ! ! req.addEventListener('readystatechange', function() {! 
! ! ! ! ! perf.timing[readyStateMap[req.readyState]] = new Date().getTime();! 
! ! ! ! }, false);! 
!! ! ! }! 
! ! ! req.addEventListener('load', function() {! 
! ! ! ! perf.timing["loadEventEnd"] = new Date().getTime();! 
! ! ! ! perf.resource.status = req.status;! 
! ! ! }, false);! 
! ! ! req.addEventListener('timeout', function() { perf.timing["timeout"] = new Date().getTime(); }, false);! 
! ! ! req.addEventListener('error', function() { perf.timing["error"] = new Date().getTime(); }, false);! 
!! ! ! req.addEventListener('abort', function() { perf.timing["abort"] = new Date().getTime(); }, false);! 
! ! ! perf.resource.name = url;! 
!! ! ! perf.resource.method = method;! 
! ! ! // call the original open method! 
! ! ! return orig_open.apply(req, arguments);! 
!! ! };! 
! ! req.send = function() {! 
!! ! ! perf.timing["requestStart"] = new Date().getTime();! 
! ! ! // call the original send method! 
! ! ! return orig_send.apply(req, arguments);! 
!! ! };! 
!! ! req.performance = perf;! 
! ! return req;! 
!! };! 
! window.XMLHttpRequest = proxy_XMLHttpRequest;! 
}
MEASURING XHRS 
!! 
function instrumentXHR 
{! 
! var proxy_XMLHttpRequest 
! orig_XMLHttpRequest 
! readyStateMap if (!orig_XMLHttpRequest 
! ! // Nothing to instrument 
! ! return 
!! }! 
! readyStateMap !! 
// We could also inherit from window.XMLHttpRequest, but for this implementation, 
! // we'll use composition 
! proxy_XMLHttpRequest 
In Short: 
Proxy XMLHttpRequest 
Capture open(),send() 
and events 
! var ! 
!! ! req 
! ! orig_open 
!! ! orig_send 
! ! req 
! ! ! 
! ! ! ! req 
! ! ! ! ! perf 
! ! ! ! 
!! ! ! 
! ! ! req 
! ! ! ! perf 
! ! ! ! perf 
! ! ! 
! ! ! req 
! ! ! req 
!! ! ! req 
! ! ! perf 
!! ! ! perf 
! ! ! 
! ! ! 
!! ! }; 
! ! req 
!! ! ! perf 
! ! ! 
! ! ! 
!! ! }; 
!! ! req 
! ! return 
!! };! 
! window.XMLHttpRequest 
}
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! 
}
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');
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;! 
}
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);! 
}
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.
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;! 
}
PERFORMANCE TIMING 
USER TIMING
USER TIMING 
AVAI LABI L ITY 
• IE >= 10 
• Chrome >= 25 
• Opera >= 15 
• Latest Opera Mobile, 
Chrome for Android, IE 
Mobile
USER TIMING EXAMPLE 
performance.mark(‘event_start');! 
! 
setTimeout(function() {! 
performance.mark('event_end');! 
performance.measure(‘time_to_event’);! 
performance.measure('event_duration','event_start',‘event_end');! 
console.log('Event took ' + ! 
performance.getEntriesByName(‘event_duration')[0].duration + ! 
' ms');! 
}, 1000);
PERFORMANCE MANAGEMENT IN 
THREE STEPS 
How Fast Am I? How Fast Should I Be? How Do I Get There?
HOW FAST SHOULD I BE?
WHAT IS A CONVERSION? 
TRACKING CONVERSIONS 
Orders 
Shares, Likes, Comments 
Page Views 
Subscriptions 
Signups 
Card Additions 
Video Plays
SPEED STRONGLY CORRELATES TO CONVERSIONS 
MEASURING THE IMPACT OF SPEED
THIS MEANS WE 
CAN MEASURE 
PATIENCE
EXAMPLE 
Time Range: 1 Month 
Median Load Time: 4.12 
Visits: 25M 
Conversion Rate: 2.71% 
Average Order: $100
SPEED INCREASES DRIVE BUSINESS IMPROVEMENTS 
CAN WE DO BETTER? 
Median Load Time: 4.12 
Total Conversion Rate: 2.71% 
Conversion Rate @ 3.0s: 4.88%
WHAT ARE WE PLAYING FOR? 
Total Conversion Rate: 2.71% 
Best Case Conversion Rate: 4.88% 
Conversion Gap: 2.32% 
Visits: 25M 
AOV: $100
(4.88% - 2.71%) * 25M * $100 = $54.25M
1 second = $54M
BUT
POTENTIAL VS REALISTIC GOALS 
100TH PERCENTILE? 
Median Load Time: 4.12 
Total Conversion Rate: 2.71% 
Conversion Rate @ 3.0s: 4.88%
REALISTIC, ITERATIVE GOALS 
Target Load Time: 4 seconds (vs 3 seconds) 
Percentile at 4 sec: 49th 
Target Percentile: 60th (vs 100th percentile) 
Percentile Gap: 11%
(4.88% - 2.71%) * (11% * 25M) * $100 = $6M
Improving from 
4.12 sec @ 50th percentile 
to 
4.0 sec @ 60th percentile 
= 
$6M / month
Beyond Page Level Metrics
Thank You
ATTRIBUTIONS 
https://secure.flickr.com/photos/torkildr/3462607995 (servers) 
https://secure.flickr.com/photos/hackny/8038587477 (real users) 
https://secure.flickr.com/photos/isherwoodchris/3096255994 (NYC) 
https://secure.flickr.com/photos/motoxgirl/11972577704 (Countryside) 
https://secure.flickr.com/photos/98640399@N08/9287370881 (Fiber Optic) 
https://secure.flickr.com/photos/secretlondon/2592690167 (Acoustic Coupler) 
https://secure.flickr.com/photos/jenny-pics/2904201123 (Rum Bottle) 
https://secure.flickr.com/photos/bekathwia/2415018504 (Privacy Sweater) 
https://secure.flickr.com/photos/zigzaglens/3566054676 (Star Field)

More Related Content

What's hot (20)

PDF
[1C1]Service Workers
NAVER D2
 
ZIP
Palestra VCR
Cássio Marques
 
PDF
You do not need automation engineer - Sqa Days - 2015 - EN
Iakiv Kramarenko
 
PPT
Service Workers for Performance
Patrick Meenan
 
PDF
PWA 與 Service Worker
Anna Su
 
PDF
Packaging Software, Puppet Labs Style - PuppetConf 2014
Puppet
 
PDF
Making the most of your Test Suite
ericholscher
 
PPT
Real Time Event Dispatcher
Peter Dietrich
 
PDF
Deploying on the cutting edge
ericholscher
 
PDF
Selenide alternative in Python - Introducing Selene [SeleniumCamp 2016]
Iakiv Kramarenko
 
KEY
Php resque
Chaitanya Kuber
 
PDF
Webscraping with asyncio
Jose Manuel Ortega Candel
 
PPT
Testing Javascript with Jasmine
Tim Tyrrell
 
KEY
Background Jobs with Resque
homanj
 
PDF
Designing net-aws-glacier
Workhorse Computing
 
PDF
Trying Continuous Delivery - pyconjp 2012
Toru Furukawa
 
PPTX
Domains!
Domenic Denicola
 
PDF
Get Grulping with JavaScript Task Runners
Matt Gifford
 
PDF
Jasmine - why JS tests don't smell fishy
Igor Napierala
 
PDF
DC |> Elixir Meetup - Going off the Rails into Elixir - Dan Ivovich
SmartLogic
 
[1C1]Service Workers
NAVER D2
 
Palestra VCR
Cássio Marques
 
You do not need automation engineer - Sqa Days - 2015 - EN
Iakiv Kramarenko
 
Service Workers for Performance
Patrick Meenan
 
PWA 與 Service Worker
Anna Su
 
Packaging Software, Puppet Labs Style - PuppetConf 2014
Puppet
 
Making the most of your Test Suite
ericholscher
 
Real Time Event Dispatcher
Peter Dietrich
 
Deploying on the cutting edge
ericholscher
 
Selenide alternative in Python - Introducing Selene [SeleniumCamp 2016]
Iakiv Kramarenko
 
Php resque
Chaitanya Kuber
 
Webscraping with asyncio
Jose Manuel Ortega Candel
 
Testing Javascript with Jasmine
Tim Tyrrell
 
Background Jobs with Resque
homanj
 
Designing net-aws-glacier
Workhorse Computing
 
Trying Continuous Delivery - pyconjp 2012
Toru Furukawa
 
Get Grulping with JavaScript Task Runners
Matt Gifford
 
Jasmine - why JS tests don't smell fishy
Igor Napierala
 
DC |> Elixir Meetup - Going off the Rails into Elixir - Dan Ivovich
SmartLogic
 

Similar to Beyond Page Level Metrics (20)

PDF
2014 06-23 velocity sc beyond page metrics
Buddy Brewer
 
PPTX
Monitoring web application response times, a new approach
Mark Friedman
 
PPTX
Using Modern Browser APIs to Improve the Performance of Your Web Applications
Nicholas Jansma
 
PPTX
Metrics, metrics everywhere (but where the heck do you start?)
SOASTA
 
PPTX
Metrics, metrics everywhere (but where the heck do you start?)
Tammy Everts
 
PPTX
Velocity NYC: Metrics, metrics everywhere (but where the heck do you start?)
Cliff Crocker
 
PPTX
Monitoring web application response times^lj a hybrid approach for windows
Mark Friedman
 
PPTX
Metrics, Metrics Everywhere (but where the heck do you start?)
SOASTA
 
PPTX
Metrics, metrics everywhere (but where the heck do you start?)
Tammy Everts
 
PPTX
Metrics, Metrics Everywhere (but where the heck do you start?)
SOASTA
 
PDF
Make It Fast - Using Modern Browser Performance APIs to Monitor and Improve t...
Nicholas Jansma
 
PPTX
Why is this ASP.NET web app running slowly?
Mark Friedman
 
PPTX
Measuring User Experience
Alois Reitbauer
 
PPTX
Measuring web performance with user-centric metrics
Giorgos Bamparopoulos
 
PDF
Measuring Real User Performance in the Browser
Nicholas Jansma
 
PPTX
Measuring User Experience in the Browser
Alois Reitbauer
 
PDF
Web Performance in the Age of HTTP2 - Topconf Tallinn 2016 - Holger Bartel
Holger Bartel
 
PPTX
The Truth About Your Web App's Performance
John Riviello
 
PDF
2014 11-18 rip onload
Buddy Brewer
 
ODP
Cvcc performance tuning
John McCaffrey
 
2014 06-23 velocity sc beyond page metrics
Buddy Brewer
 
Monitoring web application response times, a new approach
Mark Friedman
 
Using Modern Browser APIs to Improve the Performance of Your Web Applications
Nicholas Jansma
 
Metrics, metrics everywhere (but where the heck do you start?)
SOASTA
 
Metrics, metrics everywhere (but where the heck do you start?)
Tammy Everts
 
Velocity NYC: Metrics, metrics everywhere (but where the heck do you start?)
Cliff Crocker
 
Monitoring web application response times^lj a hybrid approach for windows
Mark Friedman
 
Metrics, Metrics Everywhere (but where the heck do you start?)
SOASTA
 
Metrics, metrics everywhere (but where the heck do you start?)
Tammy Everts
 
Metrics, Metrics Everywhere (but where the heck do you start?)
SOASTA
 
Make It Fast - Using Modern Browser Performance APIs to Monitor and Improve t...
Nicholas Jansma
 
Why is this ASP.NET web app running slowly?
Mark Friedman
 
Measuring User Experience
Alois Reitbauer
 
Measuring web performance with user-centric metrics
Giorgos Bamparopoulos
 
Measuring Real User Performance in the Browser
Nicholas Jansma
 
Measuring User Experience in the Browser
Alois Reitbauer
 
Web Performance in the Age of HTTP2 - Topconf Tallinn 2016 - Holger Bartel
Holger Bartel
 
The Truth About Your Web App's Performance
John Riviello
 
2014 11-18 rip onload
Buddy Brewer
 
Cvcc performance tuning
John McCaffrey
 
Ad

More from Philip Tellis (20)

PDF
Improving D3 Performance with CANVAS and other Hacks
Philip Tellis
 
PDF
Frontend Performance: Beginner to Expert to Crazy Person
Philip Tellis
 
PDF
Frontend Performance: De débutant à Expert à Fou Furieux
Philip Tellis
 
PDF
Frontend Performance: Expert to Crazy Person
Philip Tellis
 
PDF
Frontend Performance: Beginner to Expert to Crazy Person (San Diego Web Perf ...
Philip Tellis
 
PDF
Frontend Performance: Beginner to Expert to Crazy Person
Philip Tellis
 
PDF
Frontend Performance: Beginner to Expert to Crazy Person
Philip Tellis
 
PDF
Frontend Performance: Beginner to Expert to Crazy Person
Philip Tellis
 
PDF
mmm... beacons
Philip Tellis
 
PDF
RUM Distillation 101 -- Part I
Philip Tellis
 
PDF
Improving 3rd Party Script Performance With IFrames
Philip Tellis
 
PDF
Extending Boomerang
Philip Tellis
 
PDF
Abusing JavaScript to measure Web Performance, or, "how does boomerang work?"
Philip Tellis
 
PDF
The Statistics of Web Performance Analysis
Philip Tellis
 
PDF
Abusing JavaScript to Measure Web Performance
Philip Tellis
 
PDF
Rum for Breakfast
Philip Tellis
 
PDF
Analysing network characteristics with JavaScript
Philip Tellis
 
PDF
A Node.JS bag of goodies for analyzing Web Traffic
Philip Tellis
 
PDF
Input sanitization
Philip Tellis
 
PDF
Messing with JavaScript and the DOM to measure network characteristics
Philip Tellis
 
Improving D3 Performance with CANVAS and other Hacks
Philip Tellis
 
Frontend Performance: Beginner to Expert to Crazy Person
Philip Tellis
 
Frontend Performance: De débutant à Expert à Fou Furieux
Philip Tellis
 
Frontend Performance: Expert to Crazy Person
Philip Tellis
 
Frontend Performance: Beginner to Expert to Crazy Person (San Diego Web Perf ...
Philip Tellis
 
Frontend Performance: Beginner to Expert to Crazy Person
Philip Tellis
 
Frontend Performance: Beginner to Expert to Crazy Person
Philip Tellis
 
Frontend Performance: Beginner to Expert to Crazy Person
Philip Tellis
 
mmm... beacons
Philip Tellis
 
RUM Distillation 101 -- Part I
Philip Tellis
 
Improving 3rd Party Script Performance With IFrames
Philip Tellis
 
Extending Boomerang
Philip Tellis
 
Abusing JavaScript to measure Web Performance, or, "how does boomerang work?"
Philip Tellis
 
The Statistics of Web Performance Analysis
Philip Tellis
 
Abusing JavaScript to Measure Web Performance
Philip Tellis
 
Rum for Breakfast
Philip Tellis
 
Analysing network characteristics with JavaScript
Philip Tellis
 
A Node.JS bag of goodies for analyzing Web Traffic
Philip Tellis
 
Input sanitization
Philip Tellis
 
Messing with JavaScript and the DOM to measure network characteristics
Philip Tellis
 
Ad

Recently uploaded (20)

PDF
The Builder’s Playbook - 2025 State of AI Report.pdf
jeroen339954
 
PDF
Exolore The Essential AI Tools in 2025.pdf
Srinivasan M
 
PPTX
Top iOS App Development Company in the USA for Innovative Apps
SynapseIndia
 
PDF
Persuasive AI: risks and opportunities in the age of digital debate
Speck&Tech
 
PDF
Smart Air Quality Monitoring with Serrax AQM190 LITE
SERRAX TECHNOLOGIES LLP
 
PDF
Log-Based Anomaly Detection: Enhancing System Reliability with Machine Learning
Mohammed BEKKOUCHE
 
PPTX
OpenID AuthZEN - Analyst Briefing July 2025
David Brossard
 
PDF
Empower Inclusion Through Accessible Java Applications
Ana-Maria Mihalceanu
 
PPTX
Building a Production-Ready Barts Health Secure Data Environment Tooling, Acc...
Barts Health
 
PDF
New from BookNet Canada for 2025: BNC BiblioShare - Tech Forum 2025
BookNet Canada
 
PDF
NewMind AI - Journal 100 Insights After The 100th Issue
NewMind AI
 
PDF
DevBcn - Building 10x Organizations Using Modern Productivity Metrics
Justin Reock
 
PDF
July Patch Tuesday
Ivanti
 
PPTX
MSP360 Backup Scheduling and Retention Best Practices.pptx
MSP360
 
PDF
Complete Network Protection with Real-Time Security
L4RGINDIA
 
PDF
HCIP-Data Center Facility Deployment V2.0 Training Material (Without Remarks ...
mcastillo49
 
PDF
Predicting the unpredictable: re-engineering recommendation algorithms for fr...
Speck&Tech
 
PDF
Wojciech Ciemski for Top Cyber News MAGAZINE. June 2025
Dr. Ludmila Morozova-Buss
 
PPTX
AUTOMATION AND ROBOTICS IN PHARMA INDUSTRY.pptx
sameeraaabegumm
 
PDF
Complete JavaScript Notes: From Basics to Advanced Concepts.pdf
haydendavispro
 
The Builder’s Playbook - 2025 State of AI Report.pdf
jeroen339954
 
Exolore The Essential AI Tools in 2025.pdf
Srinivasan M
 
Top iOS App Development Company in the USA for Innovative Apps
SynapseIndia
 
Persuasive AI: risks and opportunities in the age of digital debate
Speck&Tech
 
Smart Air Quality Monitoring with Serrax AQM190 LITE
SERRAX TECHNOLOGIES LLP
 
Log-Based Anomaly Detection: Enhancing System Reliability with Machine Learning
Mohammed BEKKOUCHE
 
OpenID AuthZEN - Analyst Briefing July 2025
David Brossard
 
Empower Inclusion Through Accessible Java Applications
Ana-Maria Mihalceanu
 
Building a Production-Ready Barts Health Secure Data Environment Tooling, Acc...
Barts Health
 
New from BookNet Canada for 2025: BNC BiblioShare - Tech Forum 2025
BookNet Canada
 
NewMind AI - Journal 100 Insights After The 100th Issue
NewMind AI
 
DevBcn - Building 10x Organizations Using Modern Productivity Metrics
Justin Reock
 
July Patch Tuesday
Ivanti
 
MSP360 Backup Scheduling and Retention Best Practices.pptx
MSP360
 
Complete Network Protection with Real-Time Security
L4RGINDIA
 
HCIP-Data Center Facility Deployment V2.0 Training Material (Without Remarks ...
mcastillo49
 
Predicting the unpredictable: re-engineering recommendation algorithms for fr...
Speck&Tech
 
Wojciech Ciemski for Top Cyber News MAGAZINE. June 2025
Dr. Ludmila Morozova-Buss
 
AUTOMATION AND ROBOTICS IN PHARMA INDUSTRY.pptx
sameeraaabegumm
 
Complete JavaScript Notes: From Basics to Advanced Concepts.pdf
haydendavispro
 

Beyond Page Level Metrics

  • 2. Buddy Brewer @bbrewer Philip Tellis @bluesmoon
  • 3. GITHUB git clone <clone url> https://github.com/lognormal/ beyond-page-metrics https://github.com/lognormal/ boomerang
  • 4. WHAT DOES A PAGE LOOK LIKE ON THE NETWORK?
  • 5. HOW DO DIFFERENT BROWSERS HANDLE PARALLELIZATION?
  • 6. WHICH PAGE COMPONENTS AFFECT PERCEIVED LATENCY?
  • 7. ARE ANY OF THEM SPOFS? • Static JavaScript files, external CSS files • Anything that blocks onload if you have scripts that run on onload
  • 8. CAN’T WE GET THIS ALREADY? WHY DO WE NEED RUM?
  • 9. San Francisco London Paris Gilroy Tellisford Eze
  • 10. Fast Connections Slow Connections
  • 12.
  • 14. NAVIGATION TIMING AVAI LABI L ITY • IE >= 9 • FF >= 7 • Chrome >= 6 • Opera >= 15 • Latest Android, Blackberry, Opera Mobile, Chrome for Android, Firefox for Android, IE Mobile
  • 15. NAVIGATION TIMING EXAMPLE var loadEventDuration = performance.timing.loadEventEnd - ! performance.timing.loadEventStart;
  • 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 }
  • 24. MEASURING XHRS !! function instrumentXHR()! {! ! var proxy_XMLHttpRequest,! ! orig_XMLHttpRequest = window.XMLHttpRequest,! ! readyStateMap;! if (!orig_XMLHttpRequest) {! ! ! // Nothing to instrument! ! ! return;! !! }! ! readyStateMap = [ "uninitialized", "open", "responseStart", "domInteractive", "responseEnd" ];! !! // We could also inherit from window.XMLHttpRequest, but for this implementation,! ! // we'll use composition! ! proxy_XMLHttpRequest = function() {! !! ! var req, perf = { timing: {}, resource: {} }, orig_open, orig_send;! !! ! req = new orig_XMLHttpRequest;! ! ! orig_open = req.open;! !! ! orig_send = req.send;! ! ! req.open = function(method, url, async) {! ! ! ! if (async) {! ! ! ! ! req.addEventListener('readystatechange', function() {! ! ! ! ! ! perf.timing[readyStateMap[req.readyState]] = new Date().getTime();! ! ! ! ! }, false);! !! ! ! }! ! ! ! req.addEventListener('load', function() {! ! ! ! ! perf.timing["loadEventEnd"] = new Date().getTime();! ! ! ! ! perf.resource.status = req.status;! ! ! ! }, false);! ! ! ! req.addEventListener('timeout', function() { perf.timing["timeout"] = new Date().getTime(); }, false);! ! ! ! req.addEventListener('error', function() { perf.timing["error"] = new Date().getTime(); }, false);! !! ! ! req.addEventListener('abort', function() { perf.timing["abort"] = new Date().getTime(); }, false);! ! ! ! perf.resource.name = url;! !! ! ! perf.resource.method = method;! ! ! ! // call the original open method! ! ! ! return orig_open.apply(req, arguments);! !! ! };! ! ! req.send = function() {! !! ! ! perf.timing["requestStart"] = new Date().getTime();! ! ! ! // call the original send method! ! ! ! return orig_send.apply(req, arguments);! !! ! };! !! ! req.performance = perf;! ! ! return req;! !! };! ! window.XMLHttpRequest = proxy_XMLHttpRequest;! }
  • 25. MEASURING XHRS !! function instrumentXHR {! ! var proxy_XMLHttpRequest ! orig_XMLHttpRequest ! readyStateMap if (!orig_XMLHttpRequest ! ! // Nothing to instrument ! ! return !! }! ! readyStateMap !! // We could also inherit from window.XMLHttpRequest, but for this implementation, ! // we'll use composition ! proxy_XMLHttpRequest In Short: Proxy XMLHttpRequest Capture open(),send() and events ! var ! !! ! req ! ! orig_open !! ! orig_send ! ! req ! ! ! ! ! ! ! req ! ! ! ! ! perf ! ! ! ! !! ! ! ! ! ! req ! ! ! ! perf ! ! ! ! perf ! ! ! ! ! ! req ! ! ! req !! ! ! req ! ! ! perf !! ! ! perf ! ! ! ! ! ! !! ! }; ! ! req !! ! ! perf ! ! ! ! ! ! !! ! }; !! ! req ! ! return !! };! ! window.XMLHttpRequest }
  • 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;! }
  • 33. USER TIMING AVAI LABI L ITY • IE >= 10 • Chrome >= 25 • Opera >= 15 • Latest Opera Mobile, Chrome for Android, IE Mobile
  • 34. USER TIMING EXAMPLE performance.mark(‘event_start');! ! setTimeout(function() {! performance.mark('event_end');! performance.measure(‘time_to_event’);! performance.measure('event_duration','event_start',‘event_end');! console.log('Event took ' + ! performance.getEntriesByName(‘event_duration')[0].duration + ! ' ms');! }, 1000);
  • 35. PERFORMANCE MANAGEMENT IN THREE STEPS How Fast Am I? How Fast Should I Be? How Do I Get There?
  • 37. WHAT IS A CONVERSION? TRACKING CONVERSIONS Orders Shares, Likes, Comments Page Views Subscriptions Signups Card Additions Video Plays
  • 38. SPEED STRONGLY CORRELATES TO CONVERSIONS MEASURING THE IMPACT OF SPEED
  • 39. THIS MEANS WE CAN MEASURE PATIENCE
  • 40. EXAMPLE Time Range: 1 Month Median Load Time: 4.12 Visits: 25M Conversion Rate: 2.71% Average Order: $100
  • 41. SPEED INCREASES DRIVE BUSINESS IMPROVEMENTS CAN WE DO BETTER? Median Load Time: 4.12 Total Conversion Rate: 2.71% Conversion Rate @ 3.0s: 4.88%
  • 42. WHAT ARE WE PLAYING FOR? Total Conversion Rate: 2.71% Best Case Conversion Rate: 4.88% Conversion Gap: 2.32% Visits: 25M AOV: $100
  • 43. (4.88% - 2.71%) * 25M * $100 = $54.25M
  • 44. 1 second = $54M
  • 45. BUT
  • 46. POTENTIAL VS REALISTIC GOALS 100TH PERCENTILE? Median Load Time: 4.12 Total Conversion Rate: 2.71% Conversion Rate @ 3.0s: 4.88%
  • 47. REALISTIC, ITERATIVE GOALS Target Load Time: 4 seconds (vs 3 seconds) Percentile at 4 sec: 49th Target Percentile: 60th (vs 100th percentile) Percentile Gap: 11%
  • 48. (4.88% - 2.71%) * (11% * 25M) * $100 = $6M
  • 49. Improving from 4.12 sec @ 50th percentile to 4.0 sec @ 60th percentile = $6M / month
  • 52. ATTRIBUTIONS https://secure.flickr.com/photos/torkildr/3462607995 (servers) https://secure.flickr.com/photos/hackny/8038587477 (real users) https://secure.flickr.com/photos/isherwoodchris/3096255994 (NYC) https://secure.flickr.com/photos/motoxgirl/11972577704 (Countryside) https://secure.flickr.com/photos/98640399@N08/9287370881 (Fiber Optic) https://secure.flickr.com/photos/secretlondon/2592690167 (Acoustic Coupler) https://secure.flickr.com/photos/jenny-pics/2904201123 (Rum Bottle) https://secure.flickr.com/photos/bekathwia/2415018504 (Privacy Sweater) https://secure.flickr.com/photos/zigzaglens/3566054676 (Star Field)