57 lines
1.3 KiB
JavaScript
57 lines
1.3 KiB
JavaScript
/**
|
|
* Ring buffer of message-processing RTTs (ms) for planning percentiles.
|
|
* @module rtt
|
|
*/
|
|
|
|
export class RttWindow {
|
|
/**
|
|
* @param {number} [cap=400]
|
|
*/
|
|
constructor(cap = 400) {
|
|
this.cap = cap;
|
|
/** @type {number[]} */
|
|
this.samples = [];
|
|
}
|
|
|
|
add(ms) {
|
|
const n = Number(ms);
|
|
if (!Number.isFinite(n) || n < 0) return;
|
|
this.samples.push(n);
|
|
if (this.samples.length > this.cap) this.samples.splice(0, this.samples.length - this.cap);
|
|
}
|
|
|
|
stats() {
|
|
return summarizeRtt(this.samples);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {number[]} samples
|
|
*/
|
|
export function summarizeRtt(samples) {
|
|
const xs = samples.filter((n) => Number.isFinite(n) && n >= 0).slice().sort((a, b) => a - b);
|
|
if (!xs.length) {
|
|
return { count: 0, minMs: null, avgMs: null, p50Ms: null, p90Ms: null };
|
|
}
|
|
const sum = xs.reduce((a, b) => a + b, 0);
|
|
return {
|
|
count: xs.length,
|
|
minMs: round1(xs[0]),
|
|
avgMs: round1(sum / xs.length),
|
|
p50Ms: round1(percentile(xs, 0.5)),
|
|
p90Ms: round1(percentile(xs, 0.9)),
|
|
};
|
|
}
|
|
|
|
function percentile(sorted, p) {
|
|
if (sorted.length === 1) return sorted[0];
|
|
const idx = (sorted.length - 1) * p;
|
|
const lo = Math.floor(idx);
|
|
const hi = Math.ceil(idx);
|
|
if (lo === hi) return sorted[lo];
|
|
return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo);
|
|
}
|
|
|
|
function round1(n) {
|
|
return Math.round(n * 10) / 10;
|
|
}
|