146 lines
6.3 KiB
JavaScript
146 lines
6.3 KiB
JavaScript
|
"use strict";
|
||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||
|
});
|
||
|
};
|
||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||
|
/*
|
||
|
* This is a TypeScript port of the original Java version, which was written by
|
||
|
* Gil Tene as described in
|
||
|
* https://github.com/HdrHistogram/HdrHistogram
|
||
|
* and released to the public domain, as explained at
|
||
|
* http://creativecommons.org/publicdomain/zero/1.0/
|
||
|
*/
|
||
|
const fc = require("fast-check");
|
||
|
const hdr = require("./index");
|
||
|
const wasm_1 = require("./wasm");
|
||
|
const runFromStryker = __dirname.includes("stryker");
|
||
|
const runnerOptions = {
|
||
|
numRuns: runFromStryker ? 10 : 1000,
|
||
|
verbose: true,
|
||
|
};
|
||
|
describe("Histogram percentile computation", () => {
|
||
|
beforeAll(wasm_1.initWebAssembly);
|
||
|
const numberOfSignificantValueDigits = 3;
|
||
|
[true, false].forEach((useWebAssembly) => [16, "packed"].forEach((bitBucketSize) => it(`Histogram ${bitBucketSize} (wasm: ${useWebAssembly}) should be accurate according to its significant figures`, () => __awaiter(void 0, void 0, void 0, function* () {
|
||
|
yield wasm_1.initWebAssembly();
|
||
|
fc.assert(fc.property(arbData(2000), (numbers) => {
|
||
|
const histogram = hdr.build({
|
||
|
bitBucketSize,
|
||
|
numberOfSignificantValueDigits,
|
||
|
useWebAssembly,
|
||
|
});
|
||
|
numbers.forEach((n) => histogram.recordValue(n));
|
||
|
const actual = quantile(numbers, 90);
|
||
|
const got = histogram.getValueAtPercentile(90);
|
||
|
const relativeError = Math.abs(1 - got / actual);
|
||
|
const variation = Math.pow(10, -numberOfSignificantValueDigits);
|
||
|
histogram.destroy();
|
||
|
return relativeError < variation;
|
||
|
}), runnerOptions);
|
||
|
}))));
|
||
|
});
|
||
|
describe("Histogram percentile computation (packed vs classic)", () => {
|
||
|
const numberOfSignificantValueDigits = 3;
|
||
|
const classicHistogram = hdr.build({
|
||
|
numberOfSignificantValueDigits,
|
||
|
});
|
||
|
const histogram = hdr.build({
|
||
|
numberOfSignificantValueDigits,
|
||
|
bitBucketSize: "packed",
|
||
|
useWebAssembly: false,
|
||
|
});
|
||
|
it(`should be accurate according to its significant figures`, () => {
|
||
|
fc.assert(fc.property(arbData(5), (numbers) => {
|
||
|
histogram.reset();
|
||
|
classicHistogram.reset();
|
||
|
numbers.forEach((n) => histogram.recordValue(n));
|
||
|
numbers.forEach((n) => classicHistogram.recordValue(n));
|
||
|
const actual = classicHistogram.getValueAtPercentile(90);
|
||
|
const got = histogram.getValueAtPercentile(90);
|
||
|
return actual === got;
|
||
|
}), runnerOptions);
|
||
|
});
|
||
|
});
|
||
|
describe("Histogram percentile computation with CO correction (wasm vs js)", () => {
|
||
|
beforeAll(wasm_1.initWebAssembly);
|
||
|
let jsHistogram;
|
||
|
let wasmHistogram;
|
||
|
beforeEach(() => {
|
||
|
jsHistogram = hdr.build({
|
||
|
useWebAssembly: false,
|
||
|
});
|
||
|
wasmHistogram = hdr.build({
|
||
|
useWebAssembly: true,
|
||
|
});
|
||
|
});
|
||
|
afterEach(() => {
|
||
|
jsHistogram.destroy();
|
||
|
wasmHistogram.destroy();
|
||
|
});
|
||
|
it(`should be accurate according to its significant figures`, () => {
|
||
|
fc.assert(fc.property(arbData(1, 100 * 1000), (numbers) => {
|
||
|
jsHistogram.reset();
|
||
|
wasmHistogram.reset();
|
||
|
numbers.forEach((n) => {
|
||
|
jsHistogram.recordValueWithExpectedInterval(n, 1000);
|
||
|
});
|
||
|
numbers.forEach((n) => {
|
||
|
wasmHistogram.recordValueWithExpectedInterval(n, 1000);
|
||
|
});
|
||
|
const js = jsHistogram.getValueAtPercentile(90);
|
||
|
const wasm = wasmHistogram.getValueAtPercentile(90);
|
||
|
const relativeError = Math.abs(1 - js / wasm);
|
||
|
const variation = Math.pow(10, -3);
|
||
|
if (relativeError >= variation) {
|
||
|
console.log({ js, wasm });
|
||
|
}
|
||
|
return relativeError < variation;
|
||
|
}), runnerOptions);
|
||
|
});
|
||
|
});
|
||
|
describe("Histogram encoding/decoding", () => {
|
||
|
beforeAll(wasm_1.initWebAssembly);
|
||
|
const numberOfSignificantValueDigits = 3;
|
||
|
[true, false].forEach((useWebAssembly) => [8, 16, 32, 64, "packed"].forEach((bitBucketSize) => {
|
||
|
it(`Histogram ${bitBucketSize} (wasm: ${useWebAssembly}) should keep all data after an encoding/decoding roundtrip`, () => {
|
||
|
fc.assert(fc.property(arbData(1), fc.double(50, 100), (numbers, percentile) => {
|
||
|
const histogram = hdr.build({
|
||
|
bitBucketSize,
|
||
|
numberOfSignificantValueDigits,
|
||
|
useWebAssembly,
|
||
|
});
|
||
|
numbers.forEach((n) => histogram.recordValue(n));
|
||
|
const encodedHistogram = hdr.encodeIntoCompressedBase64(histogram);
|
||
|
const decodedHistogram = hdr.decodeFromCompressedBase64(encodedHistogram);
|
||
|
const actual = histogram.getValueAtPercentile(percentile);
|
||
|
const got = decodedHistogram.getValueAtPercentile(percentile);
|
||
|
histogram.destroy();
|
||
|
decodedHistogram.destroy();
|
||
|
return actual === got;
|
||
|
}), runnerOptions);
|
||
|
});
|
||
|
}));
|
||
|
});
|
||
|
const arbData = (size, max = Number.MAX_SAFE_INTEGER) => fc.array(fc.integer(1, max), size, size);
|
||
|
// reference implementation
|
||
|
const quantile = (inputData, percentile) => {
|
||
|
const data = [...inputData].sort((a, b) => a - b);
|
||
|
const index = (percentile / 100) * (data.length - 1);
|
||
|
let result;
|
||
|
if (Math.floor(index) === index) {
|
||
|
result = data[index];
|
||
|
}
|
||
|
else {
|
||
|
const i = Math.floor(index);
|
||
|
const fraction = index - i;
|
||
|
result = data[i] + (data[i + 1] - data[i]) * fraction;
|
||
|
}
|
||
|
return result;
|
||
|
};
|
||
|
//# sourceMappingURL=Histogram.fc.spec.js.map
|