Source code

Revision control

Copy as Markdown

Other Tools

import * as Statistics from "./statistics.mjs";
export const MILLISECONDS_PER_MINUTE = 60 * 1000;
export class Metric {
static separator = "/";
constructor(name, unit = "ms") {
if (typeof name !== "string")
throw new Error(`Invalid metric.name=${name}, expected string.`);
this.name = name;
this.unit = unit;
this.mean = 0.0;
this.geomean = 0.0;
this.delta = 0.0;
this.percentDelta = 0.0;
this.sum = 0.0;
this.min = 0.0;
this.max = 0.0;
this.values = [];
this.children = [];
// Mark properties which refer to other Metric objects as
// non-enumerable to avoid issue with JSON.stringify due to circular
// references.
Object.defineProperties(this, {
parent: {
writable: true,
value: undefined,
},
});
}
get shortName() {
return this.parent ? this.name.replace(`${this.parent.name}-`, "") : this.name;
}
get valueString() {
const mean = this.mean.toFixed(2);
if (!this.percentDelta || !this.delta)
return `${mean} ${this.unit}`;
return `${mean} ± ${this.deltaString} ${this.unit}`;
}
get deltaString() {
if (!this.percentDelta || !this.delta)
return "";
return `${this.delta.toFixed(2)} (${this.percentDelta.toFixed(1)}%)`;
}
get length() {
return this.values.length;
}
addChild(metric) {
if (metric.parent)
throw new Error("Cannot re-add sub metric");
metric.parent = this;
this.children.push(metric);
}
add(value) {
if (typeof value !== "number")
throw new Error(`Adding invalid value=${value} to metric=${this.name}`);
this.values.push(value);
}
computeAggregatedMetrics() {
// Avoid the loss of significance for the sum.
const sortedValues = this.values.concat().sort((a, b) => a - b);
this.sum = Statistics.sum(sortedValues);
this.min = sortedValues[0];
this.max = sortedValues[sortedValues.length - 1];
this.mean = this.sum / this.values.length;
const product = Statistics.product(sortedValues);
this.geomean = Math.pow(product, 1 / this.values.length);
if (this.values.length > 1) {
const squareSum = Statistics.squareSum(sortedValues);
this.delta = Statistics.confidenceIntervalDelta(0.95, this.values.length, this.sum, squareSum);
this.percentDelta = isNaN(this.delta) ? undefined : (this.delta * 100) / this.mean;
}
}
}