overtake 0.1.1 → 1.0.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/README.md +72 -79
  2. package/bin/overtake.js +2 -0
  3. package/build/__tests__/runner.d.ts +1 -0
  4. package/build/benchmark.cjs +237 -0
  5. package/build/benchmark.cjs.map +1 -0
  6. package/build/benchmark.d.ts +64 -0
  7. package/build/benchmark.js +189 -0
  8. package/build/benchmark.js.map +1 -0
  9. package/build/cli.cjs +149 -0
  10. package/build/cli.cjs.map +1 -0
  11. package/build/cli.d.ts +1 -0
  12. package/build/cli.js +104 -0
  13. package/build/cli.js.map +1 -0
  14. package/build/executor.cjs +68 -0
  15. package/build/executor.cjs.map +1 -0
  16. package/build/executor.d.ts +10 -0
  17. package/build/executor.js +58 -0
  18. package/build/executor.js.map +1 -0
  19. package/build/index.cjs +20 -0
  20. package/build/index.cjs.map +1 -0
  21. package/build/index.d.ts +5 -0
  22. package/build/index.js +3 -0
  23. package/build/index.js.map +1 -0
  24. package/build/queue.cjs +48 -0
  25. package/build/queue.cjs.map +1 -0
  26. package/build/queue.d.ts +3 -0
  27. package/build/queue.js +38 -0
  28. package/build/queue.js.map +1 -0
  29. package/build/reporter.cjs +175 -0
  30. package/build/reporter.cjs.map +1 -0
  31. package/build/reporter.d.ts +11 -0
  32. package/build/reporter.js +157 -0
  33. package/build/reporter.js.map +1 -0
  34. package/build/runner.cjs +92 -0
  35. package/build/runner.cjs.map +1 -0
  36. package/build/runner.d.ts +2 -0
  37. package/build/runner.js +82 -0
  38. package/build/runner.js.map +1 -0
  39. package/build/types.cjs +48 -0
  40. package/build/types.cjs.map +1 -0
  41. package/build/types.d.ts +59 -0
  42. package/build/types.js +21 -0
  43. package/build/types.js.map +1 -0
  44. package/build/utils.cjs +100 -0
  45. package/build/utils.cjs.map +1 -0
  46. package/build/utils.d.ts +20 -0
  47. package/build/utils.js +67 -0
  48. package/build/utils.js.map +1 -0
  49. package/build/worker.cjs +29 -0
  50. package/build/worker.cjs.map +1 -0
  51. package/build/worker.d.ts +1 -0
  52. package/build/worker.js +25 -0
  53. package/build/worker.js.map +1 -0
  54. package/package.json +25 -16
  55. package/src/__tests__/runner.ts +34 -0
  56. package/src/benchmark.ts +231 -0
  57. package/src/cli.ts +114 -0
  58. package/src/executor.ts +73 -0
  59. package/src/index.ts +6 -0
  60. package/src/queue.ts +42 -0
  61. package/src/reporter.ts +139 -0
  62. package/src/runner.ts +111 -0
  63. package/src/types.ts +72 -0
  64. package/src/utils.ts +65 -0
  65. package/src/worker.ts +46 -0
  66. package/tsconfig.json +17 -0
  67. package/cli.js +0 -70
  68. package/index.d.ts +0 -56
  69. package/index.js +0 -302
  70. package/runner.js +0 -3
@@ -0,0 +1,58 @@
1
+ import { Worker } from 'node:worker_threads';
2
+ import { once } from 'node:events';
3
+ import { queue } from 'async';
4
+ import { Control, CONTROL_SLOTS } from "./types.js";
5
+ import { createReport } from "./reporter.js";
6
+ import { cmp } from "./utils.js";
7
+ export const createExecutor = ({ workers, warmupCycles, maxCycles, minCycles, absThreshold, relThreshold, reportTypes })=>{
8
+ const executor = queue(async ({ setup, teardown, pre, run, post, data })=>{
9
+ const setupCode = setup?.toString();
10
+ const teardownCode = teardown?.toString();
11
+ const preCode = pre?.toString();
12
+ const runCode = run.toString();
13
+ const postCode = post?.toString();
14
+ const controlSAB = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * CONTROL_SLOTS);
15
+ const durationsSAB = new SharedArrayBuffer(BigUint64Array.BYTES_PER_ELEMENT * maxCycles);
16
+ const workerFile = new URL('./worker.js', import.meta.url);
17
+ const workerData = {
18
+ setupCode,
19
+ teardownCode,
20
+ preCode,
21
+ runCode,
22
+ postCode,
23
+ data,
24
+ warmupCycles,
25
+ minCycles,
26
+ absThreshold,
27
+ relThreshold,
28
+ controlSAB,
29
+ durationsSAB
30
+ };
31
+ const worker = new Worker(workerFile, {
32
+ workerData
33
+ });
34
+ const [exitCode] = await once(worker, 'exit');
35
+ if (exitCode !== 0) {
36
+ throw new Error(`worker exited with code ${exitCode}`);
37
+ }
38
+ const control = new Int32Array(controlSAB);
39
+ const count = control[Control.INDEX];
40
+ const durations = new BigUint64Array(durationsSAB).slice(0, count).sort(cmp);
41
+ const report = reportTypes.map((type)=>[
42
+ type,
43
+ createReport(durations, type)
44
+ ]).concat([
45
+ [
46
+ 'count',
47
+ count
48
+ ]
49
+ ]);
50
+ return Object.fromEntries(report);
51
+ }, workers);
52
+ executor.error((err)=>{
53
+ console.error(err);
54
+ });
55
+ return executor;
56
+ };
57
+
58
+ //# sourceMappingURL=executor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/executor.ts"],"sourcesContent":["import { Worker } from 'node:worker_threads';\nimport { once } from 'node:events';\nimport { queue } from 'async';\nimport { RunOptions, ReportOptions, WorkerOptions, BenchmarkOptions, Control, ReportType, ReportTypeList, CONTROL_SLOTS } from './types.js';\nimport { createReport, Report } from './reporter.js';\nimport { cmp } from './utils.js';\n\nexport type ExecutorReport<R extends ReportTypeList> =\n Record<R[number], Report> & { count: number };\n\nexport interface ExecutorOptions<R extends ReportTypeList> extends BenchmarkOptions, ReportOptions<R> {\n workers?: number;\n maxCycles?: number;\n}\n\nexport const createExecutor = <TContext, TInput, R extends ReportTypeList>({\n workers,\n warmupCycles,\n maxCycles,\n minCycles,\n absThreshold,\n relThreshold,\n reportTypes,\n}: Required<ExecutorOptions<R>>) => {\n\n const executor = queue<RunOptions<TContext, TInput>>(async ({\n setup,\n teardown,\n pre,\n run,\n post,\n data,\n }) => {\n const setupCode = setup?.toString();\n const teardownCode = teardown?.toString();\n const preCode = pre?.toString();\n const runCode = run.toString()!;\n const postCode = post?.toString();\n\n const controlSAB = new SharedArrayBuffer(\n Int32Array.BYTES_PER_ELEMENT * CONTROL_SLOTS\n );\n const durationsSAB = new SharedArrayBuffer(\n BigUint64Array.BYTES_PER_ELEMENT * maxCycles\n );\n\n const workerFile = new URL('./worker.js', import.meta.url);\n const workerData: WorkerOptions = {\n setupCode,\n teardownCode,\n preCode,\n runCode,\n postCode,\n data,\n\n warmupCycles,\n minCycles,\n absThreshold,\n relThreshold,\n\n controlSAB,\n durationsSAB,\n };\n\n const worker = new Worker(workerFile, {\n workerData,\n });\n const [exitCode] = await once(worker, 'exit');\n if (exitCode !== 0) {\n throw new Error(`worker exited with code ${exitCode}`);\n }\n\n const control = new Int32Array(controlSAB);\n const count = control[Control.INDEX];\n const durations = new BigUint64Array(durationsSAB)\n .slice(0, count)\n .sort(cmp);\n\n const report = reportTypes\n .map<[string, unknown]>((type) => [type, createReport(durations, type)] as [ReportType, Report])\n .concat([['count', count]]);\n return Object.fromEntries(report);\n }, workers);\n\n executor.error((err) => {\n console.error(err);\n });\n\n return executor;\n};\n"],"names":["Worker","once","queue","Control","CONTROL_SLOTS","createReport","cmp","createExecutor","workers","warmupCycles","maxCycles","minCycles","absThreshold","relThreshold","reportTypes","executor","setup","teardown","pre","run","post","data","setupCode","toString","teardownCode","preCode","runCode","postCode","controlSAB","SharedArrayBuffer","Int32Array","BYTES_PER_ELEMENT","durationsSAB","BigUint64Array","workerFile","URL","url","workerData","worker","exitCode","Error","control","count","INDEX","durations","slice","sort","report","map","type","concat","Object","fromEntries","error","err","console"],"mappings":"AAAA,SAASA,MAAM,QAAQ,sBAAsB;AAC7C,SAASC,IAAI,QAAQ,cAAc;AACnC,SAASC,KAAK,QAAQ,QAAQ;AAC9B,SAAqEC,OAAO,EAA8BC,aAAa,QAAQ,aAAa;AAC5I,SAASC,YAAY,QAAgB,gBAAgB;AACrD,SAASC,GAAG,QAAQ,aAAa;AAUjC,OAAO,MAAMC,iBAAiB,CAA6C,EACzEC,OAAO,EACPC,YAAY,EACZC,SAAS,EACTC,SAAS,EACTC,YAAY,EACZC,YAAY,EACZC,WAAW,EACkB;IAE7B,MAAMC,WAAWb,MAAoC,OAAO,EAC1Dc,KAAK,EACLC,QAAQ,EACRC,GAAG,EACHC,GAAG,EACHC,IAAI,EACJC,IAAI,EACL;QACC,MAAMC,YAAYN,OAAOO;QACzB,MAAMC,eAAeP,UAAUM;QAC/B,MAAME,UAAUP,KAAKK;QACrB,MAAMG,UAAUP,IAAII,QAAQ;QAC5B,MAAMI,WAAWP,MAAMG;QAEvB,MAAMK,aAAa,IAAIC,kBACrBC,WAAWC,iBAAiB,GAAG3B;QAEjC,MAAM4B,eAAe,IAAIH,kBACvBI,eAAeF,iBAAiB,GAAGrB;QAGrC,MAAMwB,aAAa,IAAIC,IAAI,eAAe,YAAYC,GAAG;QACzD,MAAMC,aAA4B;YAChCf;YACAE;YACAC;YACAC;YACAC;YACAN;YAEAZ;YACAE;YACAC;YACAC;YAEAe;YACAI;QACF;QAEA,MAAMM,SAAS,IAAItC,OAAOkC,YAAY;YACpCG;QACF;QACA,MAAM,CAACE,SAAS,GAAG,MAAMtC,KAAKqC,QAAQ;QACtC,IAAIC,aAAa,GAAG;YAClB,MAAM,IAAIC,MAAM,CAAC,wBAAwB,EAAED,UAAU;QACvD;QAEA,MAAME,UAAU,IAAIX,WAAWF;QAC/B,MAAMc,QAAQD,OAAO,CAACtC,QAAQwC,KAAK,CAAC;QACpC,MAAMC,YAAY,IAAIX,eAAeD,cAClCa,KAAK,CAAC,GAAGH,OACTI,IAAI,CAACxC;QAER,MAAMyC,SAASjC,YACZkC,GAAG,CAAoB,CAACC,OAAS;gBAACA;gBAAM5C,aAAauC,WAAWK;aAAM,EACtEC,MAAM,CAAC;YAAC;gBAAC;gBAASR;aAAM;SAAC;QAC5B,OAAOS,OAAOC,WAAW,CAACL;IAC5B,GAAGvC;IAEHO,SAASsC,KAAK,CAAC,CAACC;QACdC,QAAQF,KAAK,CAACC;IAChB;IAEA,OAAOvC;AACT,EAAE"}
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ _export_star(require("./benchmark.cjs"), exports);
6
+ function _export_star(from, to) {
7
+ Object.keys(from).forEach(function(k) {
8
+ if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
9
+ Object.defineProperty(to, k, {
10
+ enumerable: true,
11
+ get: function() {
12
+ return from[k];
13
+ }
14
+ });
15
+ }
16
+ });
17
+ return from;
18
+ }
19
+
20
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from './benchmark.js'\nimport { Benchmark as _Benchmark } from './benchmark.js';\n\ndeclare global {\n const benchmark: typeof _Benchmark[\"create\"];\n}\n"],"names":[],"mappings":";;;;qBAAc"}
@@ -0,0 +1,5 @@
1
+ export * from './benchmark.js';
2
+ import { Benchmark as _Benchmark } from './benchmark.js';
3
+ declare global {
4
+ const benchmark: typeof _Benchmark["create"];
5
+ }
package/build/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export * from "./benchmark.js";
2
+
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from './benchmark.js'\nimport { Benchmark as _Benchmark } from './benchmark.js';\n\ndeclare global {\n const benchmark: typeof _Benchmark[\"create\"];\n}\n"],"names":[],"mappings":"AAAA,cAAc,iBAAgB"}
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "createQueue", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return createQueue;
9
+ }
10
+ });
11
+ const createQueue = (worker, concurency = 1)=>{
12
+ const queue = new Set();
13
+ const processing = new Map();
14
+ const iterator = queue[Symbol.iterator]();
15
+ let next;
16
+ let counter = 0;
17
+ queueMicrotask(async ()=>{
18
+ while(true){
19
+ if (concurency > 0 && processing.size === concurency) {
20
+ await Promise.race(processing.values());
21
+ }
22
+ if (queue.size === 0) {
23
+ const { promise, resolve } = Promise.withResolvers();
24
+ next = resolve;
25
+ await promise;
26
+ }
27
+ const result = iterator.next();
28
+ if (result.done) {
29
+ break;
30
+ }
31
+ const id = counter++;
32
+ const task = Promise.resolve(worker(result.value)).catch(()=>{}).finally(()=>{
33
+ processing.delete(id);
34
+ });
35
+ processing.set(id, task);
36
+ }
37
+ });
38
+ return {
39
+ push: async (input)=>{
40
+ queue.add(input);
41
+ if (queue.size === 0) {
42
+ next?.();
43
+ }
44
+ }
45
+ };
46
+ };
47
+
48
+ //# sourceMappingURL=queue.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/queue.ts"],"sourcesContent":["export const createQueue = <T>(\n worker: (task: T) => Promise<void>,\n concurency: number = 1,\n) => {\n const queue = new Set<T>();\n const processing = new Map<number, Promise<void>>();\n const iterator = queue[Symbol.iterator]();\n\n let next: () => void;\n let counter = 0;\n\n queueMicrotask(async () => {\n while (true) {\n if (concurency > 0 && processing.size === concurency) {\n await Promise.race(processing.values());\n }\n if (queue.size === 0) {\n const { promise, resolve } = Promise.withResolvers<void>();\n next = resolve;\n await promise;\n }\n const result = iterator.next();\n if (result.done) {\n break;\n }\n const id = counter++;\n const task = Promise.resolve(worker(result.value))\n .catch(() => { })\n .finally(() => {\n processing.delete(id);\n });\n processing.set(id, task);\n }\n });\n\n return {\n push: async (input: T) => {\n queue.add(input);\n\n if (queue.size === 0) {\n next?.();\n }\n },\n };\n};\n"],"names":["createQueue","worker","concurency","queue","Set","processing","Map","iterator","Symbol","next","counter","queueMicrotask","size","Promise","race","values","promise","resolve","withResolvers","result","done","id","task","value","catch","finally","delete","set","push","input","add"],"mappings":";;;;+BAAaA;;;eAAAA;;;AAAN,MAAMA,cAAc,CACzBC,QACAC,aAAqB,CAAC;IAEtB,MAAMC,QAAQ,IAAIC;IAClB,MAAMC,aAAa,IAAIC;IACvB,MAAMC,WAAWJ,KAAK,CAACK,OAAOD,QAAQ,CAAC;IAEvC,IAAIE;IACJ,IAAIC,UAAU;IAEdC,eAAe;QACb,MAAO,KAAM;YACX,IAAIT,aAAa,KAAKG,WAAWO,IAAI,KAAKV,YAAY;gBACpD,MAAMW,QAAQC,IAAI,CAACT,WAAWU,MAAM;YACtC;YACA,IAAIZ,MAAMS,IAAI,KAAK,GAAG;gBACpB,MAAM,EAAEI,OAAO,EAAEC,OAAO,EAAE,GAAGJ,QAAQK,aAAa;gBAClDT,OAAOQ;gBACP,MAAMD;YACR;YACA,MAAMG,SAASZ,SAASE,IAAI;YAC5B,IAAIU,OAAOC,IAAI,EAAE;gBACf;YACF;YACA,MAAMC,KAAKX;YACX,MAAMY,OAAOT,QAAQI,OAAO,CAAChB,OAAOkB,OAAOI,KAAK,GAC7CC,KAAK,CAAC,KAAQ,GACdC,OAAO,CAAC;gBACPpB,WAAWqB,MAAM,CAACL;YACpB;YACFhB,WAAWsB,GAAG,CAACN,IAAIC;QACrB;IACF;IAEA,OAAO;QACLM,MAAM,OAAOC;YACX1B,MAAM2B,GAAG,CAACD;YAEV,IAAI1B,MAAMS,IAAI,KAAK,GAAG;gBACpBH;YACF;QACF;IACF;AACF"}
@@ -0,0 +1,3 @@
1
+ export declare const createQueue: <T>(worker: (task: T) => Promise<void>, concurency?: number) => {
2
+ push: (input: T) => Promise<void>;
3
+ };
package/build/queue.js ADDED
@@ -0,0 +1,38 @@
1
+ export const createQueue = (worker, concurency = 1)=>{
2
+ const queue = new Set();
3
+ const processing = new Map();
4
+ const iterator = queue[Symbol.iterator]();
5
+ let next;
6
+ let counter = 0;
7
+ queueMicrotask(async ()=>{
8
+ while(true){
9
+ if (concurency > 0 && processing.size === concurency) {
10
+ await Promise.race(processing.values());
11
+ }
12
+ if (queue.size === 0) {
13
+ const { promise, resolve } = Promise.withResolvers();
14
+ next = resolve;
15
+ await promise;
16
+ }
17
+ const result = iterator.next();
18
+ if (result.done) {
19
+ break;
20
+ }
21
+ const id = counter++;
22
+ const task = Promise.resolve(worker(result.value)).catch(()=>{}).finally(()=>{
23
+ processing.delete(id);
24
+ });
25
+ processing.set(id, task);
26
+ }
27
+ });
28
+ return {
29
+ push: async (input)=>{
30
+ queue.add(input);
31
+ if (queue.size === 0) {
32
+ next?.();
33
+ }
34
+ }
35
+ };
36
+ };
37
+
38
+ //# sourceMappingURL=queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/queue.ts"],"sourcesContent":["export const createQueue = <T>(\n worker: (task: T) => Promise<void>,\n concurency: number = 1,\n) => {\n const queue = new Set<T>();\n const processing = new Map<number, Promise<void>>();\n const iterator = queue[Symbol.iterator]();\n\n let next: () => void;\n let counter = 0;\n\n queueMicrotask(async () => {\n while (true) {\n if (concurency > 0 && processing.size === concurency) {\n await Promise.race(processing.values());\n }\n if (queue.size === 0) {\n const { promise, resolve } = Promise.withResolvers<void>();\n next = resolve;\n await promise;\n }\n const result = iterator.next();\n if (result.done) {\n break;\n }\n const id = counter++;\n const task = Promise.resolve(worker(result.value))\n .catch(() => { })\n .finally(() => {\n processing.delete(id);\n });\n processing.set(id, task);\n }\n });\n\n return {\n push: async (input: T) => {\n queue.add(input);\n\n if (queue.size === 0) {\n next?.();\n }\n },\n };\n};\n"],"names":["createQueue","worker","concurency","queue","Set","processing","Map","iterator","Symbol","next","counter","queueMicrotask","size","Promise","race","values","promise","resolve","withResolvers","result","done","id","task","value","catch","finally","delete","set","push","input","add"],"mappings":"AAAA,OAAO,MAAMA,cAAc,CACzBC,QACAC,aAAqB,CAAC;IAEtB,MAAMC,QAAQ,IAAIC;IAClB,MAAMC,aAAa,IAAIC;IACvB,MAAMC,WAAWJ,KAAK,CAACK,OAAOD,QAAQ,CAAC;IAEvC,IAAIE;IACJ,IAAIC,UAAU;IAEdC,eAAe;QACb,MAAO,KAAM;YACX,IAAIT,aAAa,KAAKG,WAAWO,IAAI,KAAKV,YAAY;gBACpD,MAAMW,QAAQC,IAAI,CAACT,WAAWU,MAAM;YACtC;YACA,IAAIZ,MAAMS,IAAI,KAAK,GAAG;gBACpB,MAAM,EAAEI,OAAO,EAAEC,OAAO,EAAE,GAAGJ,QAAQK,aAAa;gBAClDT,OAAOQ;gBACP,MAAMD;YACR;YACA,MAAMG,SAASZ,SAASE,IAAI;YAC5B,IAAIU,OAAOC,IAAI,EAAE;gBACf;YACF;YACA,MAAMC,KAAKX;YACX,MAAMY,OAAOT,QAAQI,OAAO,CAAChB,OAAOkB,OAAOI,KAAK,GAC7CC,KAAK,CAAC,KAAQ,GACdC,OAAO,CAAC;gBACPpB,WAAWqB,MAAM,CAACL;YACpB;YACFhB,WAAWsB,GAAG,CAACN,IAAIC;QACrB;IACF;IAEA,OAAO;QACLM,MAAM,OAAOC;YACX1B,MAAM2B,GAAG,CAACD;YAEV,IAAI1B,MAAMS,IAAI,KAAK,GAAG;gBACpBH;YACF;QACF;IACF;AACF,EAAE"}
@@ -0,0 +1,175 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ function _export(target, all) {
6
+ for(var name in all)Object.defineProperty(target, name, {
7
+ enumerable: true,
8
+ get: all[name]
9
+ });
10
+ }
11
+ _export(exports, {
12
+ Report: function() {
13
+ return Report;
14
+ },
15
+ createReport: function() {
16
+ return createReport;
17
+ }
18
+ });
19
+ const _utilscjs = require("./utils.cjs");
20
+ const units = [
21
+ {
22
+ unit: 'ns',
23
+ factor: 1
24
+ },
25
+ {
26
+ unit: 'µs',
27
+ factor: 1e3
28
+ },
29
+ {
30
+ unit: 'ms',
31
+ factor: 1e6
32
+ },
33
+ {
34
+ unit: 's',
35
+ factor: 1e9
36
+ },
37
+ {
38
+ unit: 'm',
39
+ factor: 60 * 1e9
40
+ },
41
+ {
42
+ unit: 'h',
43
+ factor: 3600 * 1e9
44
+ }
45
+ ];
46
+ function smartFixed(n) {
47
+ return n.toLocaleString(undefined, {
48
+ minimumFractionDigits: 0,
49
+ maximumFractionDigits: 2,
50
+ useGrouping: false
51
+ });
52
+ }
53
+ class Report {
54
+ type;
55
+ value;
56
+ uncertainty;
57
+ scale;
58
+ constructor(type, value, uncertainty = 0, scale = 1n){
59
+ this.type = type;
60
+ this.value = value;
61
+ this.uncertainty = uncertainty;
62
+ this.scale = scale;
63
+ }
64
+ valueOf() {
65
+ return Number((0, _utilscjs.div)(this.value, this.scale));
66
+ }
67
+ toString() {
68
+ const uncertainty = this.uncertainty ? ` ± ${smartFixed(this.uncertainty)}%` : '';
69
+ const value = this.valueOf();
70
+ if (this.type === 'ops') {
71
+ return `${smartFixed(value)} ops/s${uncertainty}`;
72
+ }
73
+ let display = value;
74
+ let unit = 'ns';
75
+ for (const { unit: u, factor } of units){
76
+ const candidate = value / factor;
77
+ if (candidate < 1000) {
78
+ display = candidate;
79
+ unit = u;
80
+ break;
81
+ }
82
+ }
83
+ return `${smartFixed(display)} ${unit}${uncertainty}`;
84
+ }
85
+ }
86
+ const createReport = (durations, type)=>{
87
+ const n = durations.length;
88
+ if (n === 0) {
89
+ return new Report(type, 0n);
90
+ }
91
+ switch(type){
92
+ case 'min':
93
+ {
94
+ return new Report(type, durations[0]);
95
+ }
96
+ case 'max':
97
+ {
98
+ return new Report(type, durations[n - 1]);
99
+ }
100
+ case 'median':
101
+ {
102
+ const mid = Math.floor(n / 2);
103
+ const med = n % 2 === 0 ? (durations[mid - 1] + durations[mid]) / 2n : durations[mid];
104
+ return new Report(type, med);
105
+ }
106
+ case 'mode':
107
+ {
108
+ const freq = new Map();
109
+ let maxCount = 0n;
110
+ let modeVal = durations[0];
111
+ for (const d of durations){
112
+ const count = (freq.get(d) || 0n) + 1n;
113
+ freq.set(d, count);
114
+ if (count > maxCount) {
115
+ maxCount = count;
116
+ modeVal = d;
117
+ }
118
+ }
119
+ let lower = modeVal;
120
+ let upper = modeVal;
121
+ const firstIdx = durations.indexOf(modeVal);
122
+ const lastIdx = durations.lastIndexOf(modeVal);
123
+ if (firstIdx > 0) lower = durations[firstIdx - 1];
124
+ if (lastIdx < n - 1) upper = durations[lastIdx + 1];
125
+ const gap = (0, _utilscjs.max)(modeVal - lower, upper - modeVal);
126
+ const uncertainty = modeVal > 0 ? Number(gap / 2n * 100n / modeVal) : 0;
127
+ return new Report(type, modeVal, uncertainty);
128
+ }
129
+ case 'ops':
130
+ {
131
+ let sum = 0n;
132
+ for (const duration of durations){
133
+ sum += duration;
134
+ }
135
+ const avgNs = sum / BigInt(n);
136
+ const nsPerSec = 1_000_000_000n;
137
+ const raw = Number(nsPerSec) / Number(avgNs);
138
+ const extra = raw < 1 ? Math.ceil(-Math.log10(raw)) : 0;
139
+ const exp = raw > 100 ? 0 : 2 + extra;
140
+ const scale = 10n ** BigInt(exp);
141
+ const value = avgNs > 0n ? nsPerSec * scale / avgNs : 0n;
142
+ const deviation = durations[n - 1] - durations[0];
143
+ const uncertainty = avgNs > 0 ? Number((0, _utilscjs.div)(deviation * scale, 2n * avgNs)) : 0;
144
+ return new Report(type, value, uncertainty, scale);
145
+ }
146
+ case 'mean':
147
+ {
148
+ let sum = 0n;
149
+ for (const duration of durations){
150
+ sum += duration;
151
+ }
152
+ const value = (0, _utilscjs.divs)(sum, BigInt(n), 1n);
153
+ return new Report(type, value);
154
+ }
155
+ default:
156
+ {
157
+ const p = Number(type.slice(1));
158
+ if (p === 0) {
159
+ return new Report(type, durations[0]);
160
+ }
161
+ if (p === 100) {
162
+ return new Report(type, durations[n - 1]);
163
+ }
164
+ const idx = Math.ceil(p / 100 * n) - 1;
165
+ const value = durations[Math.min(Math.max(idx, 0), n - 1)];
166
+ const prev = idx > 0 ? durations[idx - 1] : value;
167
+ const next = idx < n - 1 ? durations[idx + 1] : value;
168
+ const gap = (0, _utilscjs.max)(value - prev, next - value);
169
+ const uncertainty = value > 0 ? Number((0, _utilscjs.div)((0, _utilscjs.divs)(gap, 2n, 100_00n), value)) / 100 : 0;
170
+ return new Report(type, value, uncertainty);
171
+ }
172
+ }
173
+ };
174
+
175
+ //# sourceMappingURL=reporter.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/reporter.ts"],"sourcesContent":["import { div, max, divs } from './utils.js';\nimport { ReportType } from './types.js';\n\nconst units = [\n { unit: 'ns', factor: 1 },\n { unit: 'µs', factor: 1e3 },\n { unit: 'ms', factor: 1e6 },\n { unit: 's', factor: 1e9 },\n { unit: 'm', factor: 60 * 1e9 },\n { unit: 'h', factor: 3600 * 1e9 },\n] as const;\n\nfunction smartFixed(n: number): string {\n return n.toLocaleString(undefined, {\n minimumFractionDigits: 0,\n maximumFractionDigits: 2,\n useGrouping: false,\n });\n}\nexport class Report {\n constructor(\n public readonly type: ReportType,\n public readonly value: bigint,\n public readonly uncertainty: number = 0,\n public readonly scale: bigint = 1n,\n ) {\n }\n valueOf() {\n return Number(div(this.value, this.scale));\n }\n toString() {\n const uncertainty = this.uncertainty\n ? ` ± ${smartFixed(this.uncertainty)}%`\n : '';\n\n const value = this.valueOf();\n if (this.type === 'ops') {\n return `${smartFixed(value)} ops/s${uncertainty}`;\n }\n let display = value;\n let unit = 'ns';\n\n for (const { unit: u, factor } of units) {\n const candidate = value / factor;\n if (candidate < 1000) {\n display = candidate;\n unit = u;\n break;\n }\n }\n return `${smartFixed(display)} ${unit}${uncertainty}`;\n }\n}\n\nexport const createReport = (durations: BigUint64Array, type: ReportType): Report => {\n const n = durations.length;\n if (n === 0) {\n return new Report(\n type,\n 0n,\n );\n }\n switch (type) {\n case 'min': {\n return new Report(type, durations[0]);\n }\n case 'max': {\n return new Report(type, durations[n - 1]);\n }\n case 'median': {\n const mid = Math.floor(n / 2);\n const med = n % 2 === 0\n ? (durations[mid - 1] + durations[mid]) / 2n\n : durations[mid];\n return new Report(type, med);\n }\n\n case 'mode': {\n const freq = new Map<bigint, bigint>();\n let maxCount = 0n;\n let modeVal = durations[0];\n for (const d of durations) {\n const count = (freq.get(d) || 0n) + 1n;\n freq.set(d, count);\n if (count > maxCount) {\n maxCount = count;\n modeVal = d;\n }\n }\n let lower = modeVal;\n let upper = modeVal;\n const firstIdx = durations.indexOf(modeVal);\n const lastIdx = durations.lastIndexOf(modeVal);\n if (firstIdx > 0) lower = durations[firstIdx - 1];\n if (lastIdx < n - 1) upper = durations[lastIdx + 1];\n const gap = max(modeVal - lower, upper - modeVal);\n const uncertainty = modeVal > 0\n ? Number((gap / 2n * 100n) / modeVal)\n : 0;\n return new Report(type, modeVal, uncertainty);\n }\n\n case 'ops': {\n let sum = 0n;\n for (const duration of durations) {\n sum += duration;\n }\n const avgNs = sum / BigInt(n);\n const nsPerSec = 1_000_000_000n;\n const raw = Number(nsPerSec) / Number(avgNs);\n const extra = raw < 1\n ? Math.ceil(-Math.log10(raw))\n : 0;\n\n const exp = raw > 100\n ? 0\n : 2 + extra;\n\n const scale = 10n ** BigInt(exp);\n\n const value = avgNs > 0n\n ? (nsPerSec * scale) / avgNs\n : 0n;\n const deviation = durations[n - 1] - durations[0];\n const uncertainty = avgNs > 0 ? Number(div(deviation * scale, 2n * avgNs)) : 0;\n return new Report(type, value, uncertainty, scale);\n }\n case 'mean': {\n let sum = 0n;\n for (const duration of durations) {\n sum += duration;\n }\n const value = divs(sum, BigInt(n), 1n);\n return new Report(type, value);\n }\n\n default: {\n const p = Number(type.slice(1));\n if (p === 0) {\n return new Report(type, durations[0]);\n }\n if (p === 100) {\n return new Report(type, durations[n - 1]);\n }\n const idx = Math.ceil((p / 100) * n) - 1;\n const value = durations[Math.min(Math.max(idx, 0), n - 1)];\n const prev = idx > 0 ? durations[idx - 1] : value;\n const next = idx < n - 1 ? durations[idx + 1] : value;\n const gap = max(value - prev, next - value);\n const uncertainty = value > 0 ? Number(div(divs(gap, 2n, 100_00n), value)) / 100 : 0;\n\n return new Report(type, value, uncertainty);\n }\n }\n};\n"],"names":["Report","createReport","units","unit","factor","smartFixed","n","toLocaleString","undefined","minimumFractionDigits","maximumFractionDigits","useGrouping","constructor","type","value","uncertainty","scale","valueOf","Number","div","toString","display","u","candidate","durations","length","mid","Math","floor","med","freq","Map","maxCount","modeVal","d","count","get","set","lower","upper","firstIdx","indexOf","lastIdx","lastIndexOf","gap","max","sum","duration","avgNs","BigInt","nsPerSec","raw","extra","ceil","log10","exp","deviation","divs","p","slice","idx","min","prev","next"],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { ReportType } from './types.js';
2
+ export declare class Report {
3
+ readonly type: ReportType;
4
+ readonly value: bigint;
5
+ readonly uncertainty: number;
6
+ readonly scale: bigint;
7
+ constructor(type: ReportType, value: bigint, uncertainty?: number, scale?: bigint);
8
+ valueOf(): number;
9
+ toString(): string;
10
+ }
11
+ export declare const createReport: (durations: BigUint64Array, type: ReportType) => Report;
@@ -0,0 +1,157 @@
1
+ import { div, max, divs } from "./utils.js";
2
+ const units = [
3
+ {
4
+ unit: 'ns',
5
+ factor: 1
6
+ },
7
+ {
8
+ unit: 'µs',
9
+ factor: 1e3
10
+ },
11
+ {
12
+ unit: 'ms',
13
+ factor: 1e6
14
+ },
15
+ {
16
+ unit: 's',
17
+ factor: 1e9
18
+ },
19
+ {
20
+ unit: 'm',
21
+ factor: 60 * 1e9
22
+ },
23
+ {
24
+ unit: 'h',
25
+ factor: 3600 * 1e9
26
+ }
27
+ ];
28
+ function smartFixed(n) {
29
+ return n.toLocaleString(undefined, {
30
+ minimumFractionDigits: 0,
31
+ maximumFractionDigits: 2,
32
+ useGrouping: false
33
+ });
34
+ }
35
+ export class Report {
36
+ type;
37
+ value;
38
+ uncertainty;
39
+ scale;
40
+ constructor(type, value, uncertainty = 0, scale = 1n){
41
+ this.type = type;
42
+ this.value = value;
43
+ this.uncertainty = uncertainty;
44
+ this.scale = scale;
45
+ }
46
+ valueOf() {
47
+ return Number(div(this.value, this.scale));
48
+ }
49
+ toString() {
50
+ const uncertainty = this.uncertainty ? ` ± ${smartFixed(this.uncertainty)}%` : '';
51
+ const value = this.valueOf();
52
+ if (this.type === 'ops') {
53
+ return `${smartFixed(value)} ops/s${uncertainty}`;
54
+ }
55
+ let display = value;
56
+ let unit = 'ns';
57
+ for (const { unit: u, factor } of units){
58
+ const candidate = value / factor;
59
+ if (candidate < 1000) {
60
+ display = candidate;
61
+ unit = u;
62
+ break;
63
+ }
64
+ }
65
+ return `${smartFixed(display)} ${unit}${uncertainty}`;
66
+ }
67
+ }
68
+ export const createReport = (durations, type)=>{
69
+ const n = durations.length;
70
+ if (n === 0) {
71
+ return new Report(type, 0n);
72
+ }
73
+ switch(type){
74
+ case 'min':
75
+ {
76
+ return new Report(type, durations[0]);
77
+ }
78
+ case 'max':
79
+ {
80
+ return new Report(type, durations[n - 1]);
81
+ }
82
+ case 'median':
83
+ {
84
+ const mid = Math.floor(n / 2);
85
+ const med = n % 2 === 0 ? (durations[mid - 1] + durations[mid]) / 2n : durations[mid];
86
+ return new Report(type, med);
87
+ }
88
+ case 'mode':
89
+ {
90
+ const freq = new Map();
91
+ let maxCount = 0n;
92
+ let modeVal = durations[0];
93
+ for (const d of durations){
94
+ const count = (freq.get(d) || 0n) + 1n;
95
+ freq.set(d, count);
96
+ if (count > maxCount) {
97
+ maxCount = count;
98
+ modeVal = d;
99
+ }
100
+ }
101
+ let lower = modeVal;
102
+ let upper = modeVal;
103
+ const firstIdx = durations.indexOf(modeVal);
104
+ const lastIdx = durations.lastIndexOf(modeVal);
105
+ if (firstIdx > 0) lower = durations[firstIdx - 1];
106
+ if (lastIdx < n - 1) upper = durations[lastIdx + 1];
107
+ const gap = max(modeVal - lower, upper - modeVal);
108
+ const uncertainty = modeVal > 0 ? Number(gap / 2n * 100n / modeVal) : 0;
109
+ return new Report(type, modeVal, uncertainty);
110
+ }
111
+ case 'ops':
112
+ {
113
+ let sum = 0n;
114
+ for (const duration of durations){
115
+ sum += duration;
116
+ }
117
+ const avgNs = sum / BigInt(n);
118
+ const nsPerSec = 1_000_000_000n;
119
+ const raw = Number(nsPerSec) / Number(avgNs);
120
+ const extra = raw < 1 ? Math.ceil(-Math.log10(raw)) : 0;
121
+ const exp = raw > 100 ? 0 : 2 + extra;
122
+ const scale = 10n ** BigInt(exp);
123
+ const value = avgNs > 0n ? nsPerSec * scale / avgNs : 0n;
124
+ const deviation = durations[n - 1] - durations[0];
125
+ const uncertainty = avgNs > 0 ? Number(div(deviation * scale, 2n * avgNs)) : 0;
126
+ return new Report(type, value, uncertainty, scale);
127
+ }
128
+ case 'mean':
129
+ {
130
+ let sum = 0n;
131
+ for (const duration of durations){
132
+ sum += duration;
133
+ }
134
+ const value = divs(sum, BigInt(n), 1n);
135
+ return new Report(type, value);
136
+ }
137
+ default:
138
+ {
139
+ const p = Number(type.slice(1));
140
+ if (p === 0) {
141
+ return new Report(type, durations[0]);
142
+ }
143
+ if (p === 100) {
144
+ return new Report(type, durations[n - 1]);
145
+ }
146
+ const idx = Math.ceil(p / 100 * n) - 1;
147
+ const value = durations[Math.min(Math.max(idx, 0), n - 1)];
148
+ const prev = idx > 0 ? durations[idx - 1] : value;
149
+ const next = idx < n - 1 ? durations[idx + 1] : value;
150
+ const gap = max(value - prev, next - value);
151
+ const uncertainty = value > 0 ? Number(div(divs(gap, 2n, 100_00n), value)) / 100 : 0;
152
+ return new Report(type, value, uncertainty);
153
+ }
154
+ }
155
+ };
156
+
157
+ //# sourceMappingURL=reporter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/reporter.ts"],"sourcesContent":["import { div, max, divs } from './utils.js';\nimport { ReportType } from './types.js';\n\nconst units = [\n { unit: 'ns', factor: 1 },\n { unit: 'µs', factor: 1e3 },\n { unit: 'ms', factor: 1e6 },\n { unit: 's', factor: 1e9 },\n { unit: 'm', factor: 60 * 1e9 },\n { unit: 'h', factor: 3600 * 1e9 },\n] as const;\n\nfunction smartFixed(n: number): string {\n return n.toLocaleString(undefined, {\n minimumFractionDigits: 0,\n maximumFractionDigits: 2,\n useGrouping: false,\n });\n}\nexport class Report {\n constructor(\n public readonly type: ReportType,\n public readonly value: bigint,\n public readonly uncertainty: number = 0,\n public readonly scale: bigint = 1n,\n ) {\n }\n valueOf() {\n return Number(div(this.value, this.scale));\n }\n toString() {\n const uncertainty = this.uncertainty\n ? ` ± ${smartFixed(this.uncertainty)}%`\n : '';\n\n const value = this.valueOf();\n if (this.type === 'ops') {\n return `${smartFixed(value)} ops/s${uncertainty}`;\n }\n let display = value;\n let unit = 'ns';\n\n for (const { unit: u, factor } of units) {\n const candidate = value / factor;\n if (candidate < 1000) {\n display = candidate;\n unit = u;\n break;\n }\n }\n return `${smartFixed(display)} ${unit}${uncertainty}`;\n }\n}\n\nexport const createReport = (durations: BigUint64Array, type: ReportType): Report => {\n const n = durations.length;\n if (n === 0) {\n return new Report(\n type,\n 0n,\n );\n }\n switch (type) {\n case 'min': {\n return new Report(type, durations[0]);\n }\n case 'max': {\n return new Report(type, durations[n - 1]);\n }\n case 'median': {\n const mid = Math.floor(n / 2);\n const med = n % 2 === 0\n ? (durations[mid - 1] + durations[mid]) / 2n\n : durations[mid];\n return new Report(type, med);\n }\n\n case 'mode': {\n const freq = new Map<bigint, bigint>();\n let maxCount = 0n;\n let modeVal = durations[0];\n for (const d of durations) {\n const count = (freq.get(d) || 0n) + 1n;\n freq.set(d, count);\n if (count > maxCount) {\n maxCount = count;\n modeVal = d;\n }\n }\n let lower = modeVal;\n let upper = modeVal;\n const firstIdx = durations.indexOf(modeVal);\n const lastIdx = durations.lastIndexOf(modeVal);\n if (firstIdx > 0) lower = durations[firstIdx - 1];\n if (lastIdx < n - 1) upper = durations[lastIdx + 1];\n const gap = max(modeVal - lower, upper - modeVal);\n const uncertainty = modeVal > 0\n ? Number((gap / 2n * 100n) / modeVal)\n : 0;\n return new Report(type, modeVal, uncertainty);\n }\n\n case 'ops': {\n let sum = 0n;\n for (const duration of durations) {\n sum += duration;\n }\n const avgNs = sum / BigInt(n);\n const nsPerSec = 1_000_000_000n;\n const raw = Number(nsPerSec) / Number(avgNs);\n const extra = raw < 1\n ? Math.ceil(-Math.log10(raw))\n : 0;\n\n const exp = raw > 100\n ? 0\n : 2 + extra;\n\n const scale = 10n ** BigInt(exp);\n\n const value = avgNs > 0n\n ? (nsPerSec * scale) / avgNs\n : 0n;\n const deviation = durations[n - 1] - durations[0];\n const uncertainty = avgNs > 0 ? Number(div(deviation * scale, 2n * avgNs)) : 0;\n return new Report(type, value, uncertainty, scale);\n }\n case 'mean': {\n let sum = 0n;\n for (const duration of durations) {\n sum += duration;\n }\n const value = divs(sum, BigInt(n), 1n);\n return new Report(type, value);\n }\n\n default: {\n const p = Number(type.slice(1));\n if (p === 0) {\n return new Report(type, durations[0]);\n }\n if (p === 100) {\n return new Report(type, durations[n - 1]);\n }\n const idx = Math.ceil((p / 100) * n) - 1;\n const value = durations[Math.min(Math.max(idx, 0), n - 1)];\n const prev = idx > 0 ? durations[idx - 1] : value;\n const next = idx < n - 1 ? durations[idx + 1] : value;\n const gap = max(value - prev, next - value);\n const uncertainty = value > 0 ? Number(div(divs(gap, 2n, 100_00n), value)) / 100 : 0;\n\n return new Report(type, value, uncertainty);\n }\n }\n};\n"],"names":["div","max","divs","units","unit","factor","smartFixed","n","toLocaleString","undefined","minimumFractionDigits","maximumFractionDigits","useGrouping","Report","constructor","type","value","uncertainty","scale","valueOf","Number","toString","display","u","candidate","createReport","durations","length","mid","Math","floor","med","freq","Map","maxCount","modeVal","d","count","get","set","lower","upper","firstIdx","indexOf","lastIdx","lastIndexOf","gap","sum","duration","avgNs","BigInt","nsPerSec","raw","extra","ceil","log10","exp","deviation","p","slice","idx","min","prev","next"],"mappings":"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"}