Histogram.fc.spec.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. "use strict";
  2. var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
  3. function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
  4. return new (P || (P = Promise))(function (resolve, reject) {
  5. function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
  6. function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
  7. function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
  8. step((generator = generator.apply(thisArg, _arguments || [])).next());
  9. });
  10. };
  11. Object.defineProperty(exports, "__esModule", { value: true });
  12. /*
  13. * This is a TypeScript port of the original Java version, which was written by
  14. * Gil Tene as described in
  15. * https://github.com/HdrHistogram/HdrHistogram
  16. * and released to the public domain, as explained at
  17. * http://creativecommons.org/publicdomain/zero/1.0/
  18. */
  19. const fc = require("fast-check");
  20. const hdr = require("./index");
  21. const wasm_1 = require("./wasm");
  22. const runFromStryker = __dirname.includes("stryker");
  23. const runnerOptions = {
  24. numRuns: runFromStryker ? 10 : 1000,
  25. verbose: true,
  26. };
  27. describe("Histogram percentile computation", () => {
  28. beforeAll(wasm_1.initWebAssembly);
  29. const numberOfSignificantValueDigits = 3;
  30. [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* () {
  31. yield wasm_1.initWebAssembly();
  32. fc.assert(fc.property(arbData(2000), (numbers) => {
  33. const histogram = hdr.build({
  34. bitBucketSize,
  35. numberOfSignificantValueDigits,
  36. useWebAssembly,
  37. });
  38. numbers.forEach((n) => histogram.recordValue(n));
  39. const actual = quantile(numbers, 90);
  40. const got = histogram.getValueAtPercentile(90);
  41. const relativeError = Math.abs(1 - got / actual);
  42. const variation = Math.pow(10, -numberOfSignificantValueDigits);
  43. histogram.destroy();
  44. return relativeError < variation;
  45. }), runnerOptions);
  46. }))));
  47. });
  48. describe("Histogram percentile computation (packed vs classic)", () => {
  49. const numberOfSignificantValueDigits = 3;
  50. const classicHistogram = hdr.build({
  51. numberOfSignificantValueDigits,
  52. });
  53. const histogram = hdr.build({
  54. numberOfSignificantValueDigits,
  55. bitBucketSize: "packed",
  56. useWebAssembly: false,
  57. });
  58. it(`should be accurate according to its significant figures`, () => {
  59. fc.assert(fc.property(arbData(5), (numbers) => {
  60. histogram.reset();
  61. classicHistogram.reset();
  62. numbers.forEach((n) => histogram.recordValue(n));
  63. numbers.forEach((n) => classicHistogram.recordValue(n));
  64. const actual = classicHistogram.getValueAtPercentile(90);
  65. const got = histogram.getValueAtPercentile(90);
  66. return actual === got;
  67. }), runnerOptions);
  68. });
  69. });
  70. describe("Histogram percentile computation with CO correction (wasm vs js)", () => {
  71. beforeAll(wasm_1.initWebAssembly);
  72. let jsHistogram;
  73. let wasmHistogram;
  74. beforeEach(() => {
  75. jsHistogram = hdr.build({
  76. useWebAssembly: false,
  77. });
  78. wasmHistogram = hdr.build({
  79. useWebAssembly: true,
  80. });
  81. });
  82. afterEach(() => {
  83. jsHistogram.destroy();
  84. wasmHistogram.destroy();
  85. });
  86. it(`should be accurate according to its significant figures`, () => {
  87. fc.assert(fc.property(arbData(1, 100 * 1000), (numbers) => {
  88. jsHistogram.reset();
  89. wasmHistogram.reset();
  90. numbers.forEach((n) => {
  91. jsHistogram.recordValueWithExpectedInterval(n, 1000);
  92. });
  93. numbers.forEach((n) => {
  94. wasmHistogram.recordValueWithExpectedInterval(n, 1000);
  95. });
  96. const js = jsHistogram.getValueAtPercentile(90);
  97. const wasm = wasmHistogram.getValueAtPercentile(90);
  98. const relativeError = Math.abs(1 - js / wasm);
  99. const variation = Math.pow(10, -3);
  100. if (relativeError >= variation) {
  101. console.log({ js, wasm });
  102. }
  103. return relativeError < variation;
  104. }), runnerOptions);
  105. });
  106. });
  107. describe("Histogram encoding/decoding", () => {
  108. beforeAll(wasm_1.initWebAssembly);
  109. const numberOfSignificantValueDigits = 3;
  110. [true, false].forEach((useWebAssembly) => [8, 16, 32, 64, "packed"].forEach((bitBucketSize) => {
  111. it(`Histogram ${bitBucketSize} (wasm: ${useWebAssembly}) should keep all data after an encoding/decoding roundtrip`, () => {
  112. fc.assert(fc.property(arbData(1), fc.double(50, 100), (numbers, percentile) => {
  113. const histogram = hdr.build({
  114. bitBucketSize,
  115. numberOfSignificantValueDigits,
  116. useWebAssembly,
  117. });
  118. numbers.forEach((n) => histogram.recordValue(n));
  119. const encodedHistogram = hdr.encodeIntoCompressedBase64(histogram);
  120. const decodedHistogram = hdr.decodeFromCompressedBase64(encodedHistogram);
  121. const actual = histogram.getValueAtPercentile(percentile);
  122. const got = decodedHistogram.getValueAtPercentile(percentile);
  123. histogram.destroy();
  124. decodedHistogram.destroy();
  125. return actual === got;
  126. }), runnerOptions);
  127. });
  128. }));
  129. });
  130. const arbData = (size, max = Number.MAX_SAFE_INTEGER) => fc.array(fc.integer(1, max), size, size);
  131. // reference implementation
  132. const quantile = (inputData, percentile) => {
  133. const data = [...inputData].sort((a, b) => a - b);
  134. const index = (percentile / 100) * (data.length - 1);
  135. let result;
  136. if (Math.floor(index) === index) {
  137. result = data[index];
  138. }
  139. else {
  140. const i = Math.floor(index);
  141. const fraction = index - i;
  142. result = data[i] + (data[i + 1] - data[i]) * fraction;
  143. }
  144. return result;
  145. };
  146. //# sourceMappingURL=Histogram.fc.spec.js.map