You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

372 lines
11 KiB

/*
* This is a AssemblyScript 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/
*/
import {
Histogram8,
Histogram16,
Storage,
PackedHistogram,
Histogram64,
} from "../Histogram";
const buildHistogram = (): Histogram8 =>
new Histogram8(
1,
9007199254740991, // Number.MAX_SAFE_INTEGER
3
);
describe("Histogram", () => {
it("should be instantiable", () => {
const h = buildHistogram();
h.autoResize;
expect<bool>(h.autoResize).toBe(false);
});
});
describe("Histogram initialization", () => {
it("should set sub bucket size", () => {
const histogram: Histogram8 = buildHistogram();
expect<u64>(histogram.subBucketCount).toBe(2048);
});
it("should set resize to false when max value specified", () => {
const histogram: Histogram8 = buildHistogram();
expect<bool>(histogram.autoResize).toBe(false);
});
it("should compute counts array length", () => {
const histogram: Histogram8 = buildHistogram();
expect<usize>(histogram.countsArrayLength).toBe(45056);
});
it("should compute bucket count", () => {
const histogram: Histogram8 = buildHistogram();
expect(histogram.bucketCount).toBe(43);
});
it("should set max value", () => {
const histogram: Histogram8 = buildHistogram();
expect(histogram.maxValue).toBe(0);
});
});
describe("Histogram internal indexes", () => {
it("should compute count index when value in first bucket", () => {
// given
const histogram: Histogram8 = buildHistogram();
// when
const index = histogram.countsArrayIndex(2000); // 2000 < 2048
expect(index).toBe(2000);
});
it("should compute count index when value outside first bucket", () => {
// given
const histogram: Histogram8 = buildHistogram();
// when
const index = histogram.countsArrayIndex(2050); // 2050 > 2048
// then
expect(index).toBe(2049);
});
it("should compute count index taking into account lowest discernible value", () => {
// given
const histogram = new Histogram8(
2000,
9007199254740991, // Number.MAX_SAFE_INTEGER
2
);
// when
const index = histogram.countsArrayIndex(16000);
// then
expect(index).toBe(15);
});
});
describe("Histogram computing statistics", () => {
it("should compute mean value", () => {
// given
const histogram = buildHistogram();
// when
histogram.recordValue(25);
histogram.recordValue(50);
histogram.recordValue(75);
// then
expect<f64>(histogram.getMean()).toBe(50);
});
it("should compute standard deviation", () => {
// given
const histogram = buildHistogram();
// when
histogram.recordValue(25);
histogram.recordValue(50);
histogram.recordValue(75);
// then
expect<f64>(histogram.getStdDeviation()).toBeGreaterThan(20.4124);
expect<f64>(histogram.getStdDeviation()).toBeLessThan(20.4125);
});
it("should compute percentiles", () => {
// given
const histogram = buildHistogram();
histogram.recordValue(123456);
histogram.recordValue(122777);
histogram.recordValue(127);
histogram.recordValue(42);
// when
const percentileValue = histogram.getValueAtPercentile(99.9);
// then
expect<u64>(percentileValue).toBeGreaterThan(123456 - 1000);
expect<u64>(percentileValue).toBeLessThan(123456 + 1000);
});
it("should compute max value", () => {
// given
const histogram = buildHistogram();
// when
histogram.recordValue(123);
// then
expect<u64>(histogram.maxValue).toBe(123);
});
it("should compute min non zero value", () => {
// given
const histogram = buildHistogram();
// when
histogram.recordValue(123);
// then
expect<u64>(histogram.minNonZeroValue).toBe(123);
});
it("should compute percentile distribution", () => {
// given
const histogram = buildHistogram();
// when
histogram.recordValue(25);
histogram.recordValue(50);
histogram.recordValue(75);
// then
const expectedResult = ` Value Percentile TotalCount 1/(1-Percentile)
25.000 0.000000000000 1 1.00
25.000 0.100000000000 1 1.11
25.000 0.200000000000 1 1.25
25.000 0.300000000000 1 1.43
50.000 0.400000000000 2 1.67
50.000 0.500000000000 2 2.00
50.000 0.550000000000 2 2.22
50.000 0.600000000000 2 2.50
50.000 0.650000000000 2 2.86
75.000 0.700000000000 3 3.33
75.000 1.000000000000 3
#[Mean = 50.000, StdDeviation = 20.412]
#[Max = 75.000, Total count = 3]
#[Buckets = 43, SubBuckets = 2048]
`;
expect<string>(histogram.outputPercentileDistribution()).toBe(
expectedResult
);
});
});
describe("Histogram resize", () => {
it("should not crash when autoresize on and value bigger than max", () => {
expect(() => {
// given
const histogram = new Histogram8(1, 4096, 3);
histogram.autoResize = true;
// when
histogram.recordValue(900000);
// then
expect<u64>(histogram.totalCount).toBe(1);
}).not.toThrow();
});
it("should compute percentiles after resize", () => {
// given
const histogram = new Histogram8(1, 4096, 3);
histogram.autoResize = true;
// when
histogram.recordValue(900000);
histogram.recordValue(9000000);
histogram.recordValue(9000000);
histogram.recordValue(90000000);
// then
const medianValue = histogram.getValueAtPercentile(50);
expect<f64>(Math.floor(<f64>medianValue / <f64>10000)).toBe(900);
});
it("should update highest trackable value when resizing", () => {
// given
const histogram = new Histogram8(1, 4096, 3);
histogram.autoResize = true;
// when
histogram.recordValue(9000);
// then
expect(histogram.highestTrackableValue).toBeGreaterThan(4096);
});
});
describe("Histogram clearing support", () => {
it("should reset data in order to reuse histogram", () => {
// given
const histogram = buildHistogram();
histogram.startTimeStampMsec = 42;
histogram.endTimeStampMsec = 56;
histogram.tag = "blabla";
histogram.recordValue(1000);
// when
histogram.reset();
// then
expect(histogram.totalCount).toBe(0);
expect(histogram.startTimeStampMsec).toBe(0);
expect(histogram.endTimeStampMsec).toBe(0);
//expect(histogram.tag).toBe(NO_TAG);
expect(histogram.maxValue).toBe(0);
expect(histogram.minNonZeroValue).toBe(U64.MAX_VALUE);
expect(histogram.getValueAtPercentile(99.999)).toBe(0);
});
});
describe("Histogram correcting coordinated omissions", () => {
it("should generate additional values when recording", () => {
// given
const histogram = buildHistogram();
// when
histogram.recordSingleValueWithExpectedInterval(200, 100);
// then
expect(histogram.totalCount).toBe(2);
expect(histogram.minNonZeroValue).toBe(100);
expect(histogram.maxValue).toBe(200);
});
it("should not generate additional values when recording without ommission", () => {
// given
const histogram = buildHistogram();
// when
histogram.recordSingleValueWithExpectedInterval(99, 100);
// then
expect(histogram.totalCount).toBe(1);
});
it("should generate additional values when correcting after recording", () => {
// given
const histogram = buildHistogram();
histogram.recordValue(207);
histogram.recordValue(207);
// when
const correctedHistogram = histogram.copyCorrectedForCoordinatedOmission(
100
);
// then
expect(correctedHistogram.totalCount).toBe(4);
expect(correctedHistogram.minNonZeroValue).toBe(107);
expect(correctedHistogram.maxValue).toBe(207);
});
it("should generate additional values when correcting after recording bis", () => {
// given
const histogram = buildHistogram();
histogram.recordValue(207);
histogram.recordValue(207);
// when
const correctedHistogram = histogram.copyCorrectedForCoordinatedOmission(
1000
);
// then
expect(correctedHistogram.totalCount).toBe(2);
expect(correctedHistogram.minNonZeroValue).toBe(207);
expect(correctedHistogram.maxValue).toBe(207);
});
});
describe("Histogram add & subtract", () => {
it("should add histograms of same size", () => {
// given
const histogram = buildHistogram();
const histogram2 = new Histogram16(1, 256, 3);
histogram.recordValue(42);
histogram2.recordValue(158);
// testwhen
histogram.add<Storage<Uint16Array, u16>, u16>(histogram2);
// then
expect(histogram.totalCount).toBe(2);
expect(histogram.getMean()).toBe(100);
});
it("should add histograms of different sizes & precisions", () => {
// given
const histogram = buildHistogram();
const histogram2 = new Histogram16(1, 1024, 3);
histogram2.autoResize = true;
histogram.recordValue(42000);
histogram2.recordValue(1000);
// when
histogram.add<Storage<Uint16Array, u16>, u16>(histogram2);
// then
expect(histogram.totalCount).toBe(2);
expect(Math.floor(histogram.getMean() / 100)).toBe(215);
});
it("should be equal when another histogram is added then subtracted with same characteristics", () => {
// given
const histogram = buildHistogram();
const histogram2 = buildHistogram();
histogram.recordCountAtValue(2, 100);
histogram2.recordCountAtValue(1, 100);
histogram.recordCountAtValue(2, 200);
histogram2.recordCountAtValue(1, 200);
histogram.recordCountAtValue(2, 300);
histogram2.recordCountAtValue(1, 300);
const outputBefore = histogram.outputPercentileDistribution();
// when
histogram.add<Storage<Uint8Array, u8>, u8>(histogram2);
histogram.subtract<Storage<Uint8Array, u8>, u8>(histogram2);
// then
expect(histogram.outputPercentileDistribution()).toBe(outputBefore);
});
it("should be equal when another histogram of lower precision is added then subtracted", () => {
// given
const histogram = new Histogram8(1, 1000000000, 5);
const histogram2 = new Histogram8(1, 1000000000, 3);
histogram.recordValue(10);
histogram2.recordValue(100000);
// when
const outputBefore = histogram.outputPercentileDistribution();
histogram.add<Storage<Uint8Array, u8>, u8>(histogram2);
histogram.subtract<Storage<Uint8Array, u8>, u8>(histogram2);
// then
expect(histogram.outputPercentileDistribution()).toBe(outputBefore);
});
});
describe("Packed Histogram", () => {
it("should compute percentiles as the non packed version", () => {
// given
const packedHistogram = new PackedHistogram(
1,
9007199254740991, // Number.MAX_SAFE_INTEGER
3
);
const histogram = new Histogram64(
1,
9007199254740991, // Number.MAX_SAFE_INTEGER
3
);
// when
histogram.recordValue(2199023255552);
packedHistogram.recordValue(2199023255552);
// then
expect<u64>(packedHistogram.getValueAtPercentile(90)).toBe(
histogram.getValueAtPercentile(90)
);
});
});