# Measuring Client-Side JavaScript Performance
## Monitoring a client !~ Monitoring a server
- Timing for each request
- Values reveal load
- Values aggregate over time
- Values can be compared
- Servers can self-regulate
- Insight into memory
## How do we measure client JS performance?
~~~js
var start = Date.now();
func();
var time = Date.now() - start;
console.log('func took', time, 'ms');
~~~
## What about fine-grained measurements?
## Performance in devTools
![JS dev tools in Chrome](/images/js-dev-tools.png)
## Performance API
~~~js
performance.mark("func-start");
func();
performance.mark("func-end");
// Make some [globally-spaced] marks
// Measure between two different marks
performance.measure("func", "func-start", "func-end");
// Get all of the measures out.
var measures = performance.getEntriesByName("func");
// In this case there is only one.
var measure = measures[0];
console.log("func took", measure.duration, "ms")
~~~
[window.performance docs](https://developer.mozilla.org/en-US/docs/Web/API/Performance/measure)
## Performance API++
[performance-plus repo](https://github.com/andjosh/performance-plus)
- Handles <IE10 browser support
- Preserves Functional API
- Basic quantitative helpers
- Other goodies
## Performance API++
~~~js
perf.start("func");
func();
perf.end("func");
console.log("func took", perf.duration("func"), "ms");
~~~
### This is a bit opaque, how can we visualize?
- Setting thresholds and then warning
- Reporting warnings to ourselves
- Remote [User timings](https://developers.google.com/analytics/devguides/collection/analyticsjs/user-timings)
### Performance measurements & [console.sparkline](https://github.com/johan/console.sparkline)
![JS console.sparkline](/images/console-sparkline.png)
## What affects the speed of your app?
## What affects the speed of your code?
- DOM access
- Other operations happening on the page
- Other operations happening on the domain
- Other operations happening on the client's browser
- Load happening on the client's machine
A single measurement/snapshot is not enough
## Aggregating measurements
~~~js
// ....
console.log("func has a sample size of",
perf.getEntriesByName("func").length);
console.log("func has a 95th percentile of",
perf.percentile("func", 0.95), "ms");
console.log("func averages", perf.mean("func"), "ms");
console.log("func has a standard deviation of",
perf.sdev("func"), "ms");
~~~
## Self-managing clients?
It's like responsive design, but for performance.
In [Redux middleware](https://redux.js.org/docs/advanced/Middleware.html), we can measure the time of actions
## Redux Performance warnings
~~~js
const THRESHOLD = 83; // ~ 5 animation frames
const perfMiddleware = store => next => action => {
perf.start(action.type);
let result = next(action);
perf.end(action.type);
const duration = perf.duration(action.type);
if (duration <= THRESHOLD) {
return result;
}
console.warn(`[perf] action ${action.type}`,
`took ${duration.toFixed(2)}ms`, {
mean: perf.mean(action.type),
sdev: perf.sdev(action.type),
samples: perf.getEntriesByName(action.type).length,
});
return result;
};
~~~
[redux-performance-plus repo](https://github.com/andjosh/redux-performance-plus)
## Self-throttling Redux
~~~js
let lag = 0;
perf.onFPS((fps) => { lag = 60 - fps; });
const THRESHOLD = 83; // ~ 5 animation frames
const perfMiddleware = store => next => action => {
const prevDuration = perf.duration(action.type);
if (!action._delayed
&& lag > 5 && prevDuration > THRESHOLD) {
action._delayed = lag;
setTimeout(() => {
store.dispatch(action);
}, Math.round(lag));
return store.getState();
}
perf.start(action.type);
let result = next(action);
perf.end(action.type);
return result;
};
~~~
[gist](https://gist.github.com/andjosh/383a08fec965a94ddd685c0345bcc605)
## A better approach
Degrade gracefully
- Remove ornaments
- Simplify animations
- Throttle event listeners
## Summary
- Collect fine-grained measures
- Aggregate and analyze data
- Make decisions based on that data
- Make your app aware of its performance
## Thanks!
https://www.andjosh.com/presents
https://officeluv.github.io