UNPKG

trakr

Version:

Minimal utility for tracking performance

318 lines 9.72 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Timer = exports.Tracker = exports.TRACER = exports.Tracer = exports.Stats = void 0; const IDENTITY = (a) => a; const NODE = typeof module !== 'undefined' && module.exports; // @ts-ignore const PERF = (NODE ? require('perf_hooks') : window).performance; // T-Distribution two-tailed critical values for 95% confidence. // http://www.itl.nist.gov/div898/handbook/eda/section3/eda3672.htm. const TABLE = [ 12.706, 4.303, 3.182, 2.776, 2.571, 2.447, 2.365, 2.306, 2.262, 2.228, 2.201, 2.179, 2.16, 2.145, 2.131, 2.12, 2.11, 2.101, 2.093, 2.086, 2.08, 2.074, 2.069, 2.064, 2.06, 2.056, 2.052, 2.048, 2.045, 2.042, ]; const TINF = 1.96; class Stats { constructor() { } // T(N): N lg N + 4N static compute(arr, pop) { const sorted = arr.slice(); // N sorted.sort((a, b) => a - b); // N lg N const sum = Stats.sum(sorted); // N const mean = Stats.mean(arr, sum); const variance = Stats.variance(arr, pop, mean); // 2N const stddev = Math.sqrt(variance); const error = Stats.standardErrorOfMean(sorted, pop, stddev); const margin = Stats.marginOfError(sorted, pop, error); // clang-format off return { cnt: sorted.length, sum, avg: mean, var: variance, std: stddev, sem: error, moe: margin, rme: Stats.relativeMarginOfError(sorted, pop, margin, mean), min: sorted[0], max: sorted[sorted.length - 1], p50: Stats.ptile(sorted, 0.50), p90: Stats.ptile(sorted, 0.90), p95: Stats.ptile(sorted, 0.95), p99: Stats.ptile(sorted, 0.99), }; // clang-format on } // T(N): N static max(arr) { let m = -Infinity; for (const a of arr) { if (a > m) m = a; } return m; } // T(N): N static min(arr) { let m = Infinity; for (const a of arr) { if (a < m) m = a; } return m; } // T(N): N static sum(arr) { if (!arr.length) return 0; return arr.reduce((acc, v) => acc + v); } // T(N): N | 1 static mean(arr, sum) { if (!arr.length) return 0; const s = typeof sum !== 'undefined' ? sum : Stats.sum(arr); return s / arr.length; } // T(N): N lg N static median(arr) { return Stats.percentile(arr, 0.5); } // T(N): N lg N + N static percentile(arr, p) { const sorted = arr.slice(); sorted.sort((a, b) => a - b); return Stats.ptile(sorted, p); } // PRE: arr = arr.sort((a, b) => a - b) static ptile(arr, p) { if (!arr.length) return 0; if (p <= 0) return arr[0]; if (p >= 1) return arr[arr.length - 1]; const index = (arr.length - 1) * p; const lower = Math.floor(index); const upper = lower + 1; const weight = index % 1; if (upper >= arr.length) return arr[lower]; return arr[lower] * (1 - weight) + arr[upper] * weight; } // T(N): 3N | 2N static variance(arr, pop, mean) { if (!arr.length) return 0; const n = pop ? arr.length : arr.length - 1; const m = typeof mean !== 'undefined' ? mean : Stats.mean(arr); return Stats.sum(arr.map(num => Math.pow(num - m, 2))) / n; } // T(N): 3N | 2N static standardDeviation(arr, pop, mean) { return Math.sqrt(Stats.variance(arr, pop, mean)); } // T(N): 3N | 1 static standardErrorOfMean(arr, pop, std) { if (!arr.length) return 0; const dev = typeof std !== 'undefined' ? std : Stats.standardDeviation(arr, pop); return dev / Math.sqrt(arr.length); } // T(N): 3N | 1 static marginOfError(arr, pop, sem) { if (!arr.length) return 0; const err = typeof sem !== 'undefined' ? sem : Stats.standardErrorOfMean(arr, pop); return err * (TABLE[(arr.length - 1) - 1] || TINF); } // T(N): 4N | 1 static relativeMarginOfError(arr, pop, moe, mean) { if (!arr.length) return 0; const margin = typeof moe !== 'undefined' ? moe : Stats.marginOfError(arr, pop); const avg = typeof mean !== 'undefined' ? mean : Stats.mean(arr); return (margin / avg) * 100; } } exports.Stats = Stats; class Tracer { constructor(traceEvents) { this.traceEvents = traceEvents; } get enabled() { var _a; return !!((_a = this.tracing) === null || _a === void 0 ? void 0 : _a.enabled); } enable(categories) { if (!this.traceEvents || this.enabled) return; categories = categories || ['node.perf']; this.tracing = this.traceEvents.createTracing({ categories }); this.tracing.enable(); } disable() { if (!this.tracing || !this.enabled) return; this.tracing.disable(); this.tracing = undefined; } } exports.Tracer = Tracer; exports.TRACER = new Tracer((() => { try { return require('trace_events'); } catch (_a) { return undefined; } })()); class Tracker { constructor() { this.counters = new Map(); } static create(options) { const buf = options === null || options === void 0 ? void 0 : options.buf; return buf ? new BoundedTracker(buf) : new UnboundedTracker(); } count(name, val) { val = val || 1; const c = this.counters.get(name); this.counters.set(name, typeof c !== 'undefined' ? (c + val) : val); } push(dists, name, val) { const d = dists.get(name) || []; if (!d.length) dists.set(name, d); d.push(val); } // T(M, N): M(N lg N + 3N) compute(dists, pop) { const stats = new Map(); for (const [name, vals] of dists.entries()) { stats.set(name, Stats.compute(vals, pop)); } return stats; } } exports.Tracker = Tracker; class UnboundedTracker extends Tracker { constructor() { super(); this.distributions = new Map(); } add(name, val) { return this.push(this.distributions, name, val); } stats(pop) { return this.compute(this.distributions, pop); } } class BoundedTracker extends Tracker { constructor(buf) { super(); this.buf = buf; this.next = { tag: 0, loc: 0, dloc: 0 }; this.tags = new Map(); this.distributions = new Map(); } add(name, val) { let tag = this.tags.get(name); if (typeof tag === 'undefined') { this.tags.set(name, (tag = this.next.tag++)); } this.buf.writeUInt8(tag, this.next.loc); this.buf.writeDoubleBE(val, this.next.loc + 1); this.next.loc += 9; } stats(pop) { if (this.next.dloc !== this.next.loc) { const names = []; for (const [name, tag] of this.tags.entries()) { names[tag] = name; } while (this.next.dloc < this.next.loc) { const name = names[this.buf.readUInt8(this.next.dloc)]; const val = this.buf.readDoubleBE(this.next.dloc + 1); this.push(this.distributions, name, val); this.next.dloc += 9; } } return this.compute(this.distributions, pop); } } class Timer { constructor(tracker, perf) { this.tracker = tracker; this.perf = perf; } static create(options) { const tracker = Tracker.create(options); const perf = (options === null || options === void 0 ? void 0 : options.perf) || PERF; const trace = (options && typeof options.trace !== 'undefined') ? !!options.trace : exports.TRACER.enabled; return trace ? new TracingTimer(tracker, perf) : new BasicTimer(tracker, perf); } count(name, val) { this.tracker.count(name, val); } get counters() { return this.tracker.counters; } get duration() { if (typeof this.started === 'undefined' || typeof this.stopped === 'undefined') { return undefined; } return this.stopped - this.started; } start() { if (!this.started) this.started = this.perf.now(); } stop() { if (!this.stopped) this.stopped = this.perf.now(); } stats(pop) { return this.tracker.stats(pop); } } exports.Timer = Timer; class BasicTimer extends Timer { constructor(tracker, perf) { super(tracker, perf); } time(name) { if (!this.started || this.stopped) return IDENTITY; const begin = this.perf.now(); return (a) => { this.tracker.add(name, this.perf.now() - begin); return a; }; } } class TracingTimer extends Timer { constructor(tracker, perf) { super(tracker, perf); } time(name) { if (!this.started || this.stopped) return IDENTITY; const b = `b|${name}`; this.perf.mark(b); const begin = this.perf.now(); return (a) => { this.tracker.add(name, this.perf.now() - begin); const e = `e|${name}`; this.perf.mark(e); this.perf.measure(name, b, e); return a; }; } } //# sourceMappingURL=index.js.map