/* # metrics profiler ## timing ``` var timer = Profiler.metric('resource:load') time.start(); ... time.end(); ``` ## counters ``` var counter = Profiler.metric('requests') counter.inc(); // 1 counter.inc(10); // 11 counter.dec() // 10 counter.dec(10) // 0 ``` ## Calls per second ``` var fps = Profiler.metric('fps') function render() { fps.mark(); } ``` */ var MAX_HISTORY = 1024; function Profiler () {} Profiler.metrics = {}; Profiler._backend = null; Profiler.get = function (name) { return Profiler.metrics[name] || { max: 0, min: Number.MAX_VALUE, avg: 0, total: 0, count: 0, last: 0, history: typeof (Float32Array) !== 'undefined' ? new Float32Array(MAX_HISTORY) : [] }; }; Profiler.backend = function (_) { Profiler._backend = _; }; Profiler.new_value = function (name, value, type, defer) { type = type || 'i'; var t = Profiler.metrics[name] = Profiler.get(name); t.max = Math.max(t.max, value); t.min = Math.min(t.min, value); t.total += value; ++t.count; t.avg = t.total / t.count; t.history[t.count % MAX_HISTORY] = value; if (!defer) { Profiler._backend && Profiler._backend([type, name, value]); } else { var n = performance.now(); // don't allow to send stats quick if (n - t.last > 1000) { Profiler._backend && Profiler._backend([type, name, t.avg]); t.last = n; } } }; Profiler.print_stats = function () { for (var k in Profiler.metrics) { var t = Profiler.metrics[k]; console.log(' === ' + k + ' === '); console.log(' max: ' + t.max); console.log(' min: ' + t.min); console.log(' avg: ' + t.avg); console.log(' count: ' + t.count); console.log(' total: ' + t.total); } }; function Metric (name) { this.t0 = null; this.name = name; this.count = 0; } Metric.prototype = { // // start a time measurement // start: function () { this.t0 = performance.now(); return this; }, // elapsed time since start was called _elapsed: function () { return performance.now() - this.t0; }, // // finish a time measurement and register it // ``start`` should be called first, if not this // function does not take effect // end: function (defer) { if (this.t0 !== null) { Profiler.new_value(this.name, this._elapsed(), 't', defer); this.t0 = null; } }, // // increments the value // qty: how many, default = 1 // inc: function (qty) { qty = qty === undefined ? 1 : qty; Profiler.new_value(this.name, qty, 'i'); }, // // decrements the value // qty: how many, default = 1 // dec: function (qty) { qty = qty === undefined ? 1 : qty; Profiler.new_value(this.name, qty, 'd'); }, // // measures how many times per second this function is called // mark: function () { ++this.count; if (this.t0 === null) { this.start(); return; } var elapsed = this._elapsed(); if (elapsed > 1) { Profiler.new_value(this.name, this.count); this.count = 0; this.start(); } } }; Profiler.metric = function (name) { return new Metric(name); }; module.exports = Profiler;