Skip to content

Instantly share code, notes, and snippets.

@oliviertassinari
Created October 26, 2018 19:03
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save oliviertassinari/e01bd863ab5913ba4acf3b4631c0e763 to your computer and use it in GitHub Desktop.
Save oliviertassinari/e01bd863ab5913ba4acf3b4631c0e763 to your computer and use it in GitHub Desktop.
Isomorphic performance metric.
import warning from 'warning'
const times = new Map()
const implementations = {
mark: {
start: name => {
times.set(name, performance.now())
performance.mark(`metric_${name}_start`)
},
end: name => {
const endMark = `metric_${name}_end`
performance.mark(endMark)
const startMark = `metric_${name}_start`
performance.measure(name, startMark, endMark)
const duration = performance.getEntriesByName(name)[0].duration
return duration
},
},
now: {
start: name => {
times.set(name, performance.now())
},
end: name => {
const time = times.get(name)
const duration = performance.now() - time
return duration
},
},
hrtime: {
start: name => {
// https://nodejs.org/api/process.html#process_process_hrtime_time
times.set(name, process.hrtime())
},
end: name => {
const time = times.get(name)
const durations = process.hrtime(time)
return durations[0] * 1e3 + durations[1] / 1e6
},
},
}
let getImplementationCache
function getImplementation() {
if (getImplementationCache) {
return getImplementationCache
}
if (typeof performance !== 'undefined' && performance.mark) {
getImplementationCache = implementations.mark
} else if (typeof performance !== 'undefined' && performance.now) {
getImplementationCache = implementations.now
} else if (process.hrtime) {
getImplementationCache = implementations.hrtime
} else {
throw new Error('No performance API available')
}
return getImplementationCache
}
class Metric {
/**
* Call to begin a measurement.
*/
static start(name) {
warning(!times.get(name), 'Recording already started')
getImplementation().start(name)
}
/**
* Returns the duration of the timing metric. The unit is milliseconds.
* @type {number}
*/
static end(name) {
if (!times.get(name)) {
throw new Error(`No such name '${name}' for metric`)
}
const duration = getImplementation().end(name)
times.delete(name)
return duration
}
name = ''
/**
* @param {string} name A name for the metric.
*/
constructor(name) {
if (!name) {
throw new Error('Please provide a metric name')
}
this.name = name
}
/**
* Call to begin a measurement.
*/
start(name) {
if (name) {
throw new Error('The name argument is not supported')
}
Metric.start(this.name)
}
/**
* Returns the duration of the timing metric. The unit is milliseconds.
* @type {number}
*/
end(name) {
if (name) {
throw new Error('The name argument is not supported')
}
return Metric.end(this.name)
}
}
export default Metric
@oliviertassinari
Copy link
Author

oliviertassinari commented Oct 26, 2018

It works on the server and the browser. Example of usage:

Ala console.time/console.timeEnd API

import Metric from './metric'

Metric.start('async work')
await job()
const duration = Metric.end('async work')

OOP API

import Metric from './metric'

const metric = new Metric('async work')
metric.start()
await job()
const duration = metric.end()

@sibelius
Copy link

npm package?

@baoduy
Copy link

baoduy commented Oct 27, 2018

Nice

@gregberge
Copy link

I don’t think Olivier want to maintain it, that’s why it is in a gist ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment