overtake 0.1.2 → 1.0.0-rc.2
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.
- package/README.md +76 -82
- package/bin/overtake.js +2 -0
- package/build/__tests__/runner.d.ts +1 -0
- package/build/benchmark.cjs +237 -0
- package/build/benchmark.cjs.map +1 -0
- package/build/benchmark.d.ts +64 -0
- package/build/benchmark.js +189 -0
- package/build/benchmark.js.map +1 -0
- package/build/cli.cjs +149 -0
- package/build/cli.cjs.map +1 -0
- package/build/cli.d.ts +1 -0
- package/build/cli.js +104 -0
- package/build/cli.js.map +1 -0
- package/build/executor.cjs +68 -0
- package/build/executor.cjs.map +1 -0
- package/build/executor.d.ts +10 -0
- package/build/executor.js +58 -0
- package/build/executor.js.map +1 -0
- package/build/index.cjs +20 -0
- package/build/index.cjs.map +1 -0
- package/build/index.d.ts +5 -0
- package/build/index.js +3 -0
- package/build/index.js.map +1 -0
- package/build/queue.cjs +48 -0
- package/build/queue.cjs.map +1 -0
- package/build/queue.d.ts +3 -0
- package/build/queue.js +38 -0
- package/build/queue.js.map +1 -0
- package/build/reporter.cjs +175 -0
- package/build/reporter.cjs.map +1 -0
- package/build/reporter.d.ts +11 -0
- package/build/reporter.js +157 -0
- package/build/reporter.js.map +1 -0
- package/build/runner.cjs +92 -0
- package/build/runner.cjs.map +1 -0
- package/build/runner.d.ts +2 -0
- package/build/runner.js +82 -0
- package/build/runner.js.map +1 -0
- package/build/types.cjs +48 -0
- package/build/types.cjs.map +1 -0
- package/build/types.d.ts +59 -0
- package/build/types.js +21 -0
- package/build/types.js.map +1 -0
- package/build/utils.cjs +100 -0
- package/build/utils.cjs.map +1 -0
- package/build/utils.d.ts +20 -0
- package/build/utils.js +67 -0
- package/build/utils.js.map +1 -0
- package/build/worker.cjs +29 -0
- package/build/worker.cjs.map +1 -0
- package/build/worker.d.ts +1 -0
- package/build/worker.js +25 -0
- package/build/worker.js.map +1 -0
- package/package.json +15 -13
- package/src/__tests__/runner.ts +34 -0
- package/src/benchmark.ts +231 -0
- package/src/cli.ts +114 -0
- package/src/executor.ts +73 -0
- package/src/index.ts +6 -0
- package/src/queue.ts +42 -0
- package/src/reporter.ts +139 -0
- package/src/runner.ts +111 -0
- package/src/types.ts +72 -0
- package/src/utils.ts +65 -0
- package/src/worker.ts +46 -0
- package/tsconfig.json +17 -0
- package/cli.js +0 -70
- package/index.d.ts +0 -56
- package/index.js +0 -303
- 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> = 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 const executor = queue<RunOptions<TContext, TInput>>(async ({ setup, teardown, pre, run, post, data }) => {\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(Int32Array.BYTES_PER_ELEMENT * CONTROL_SLOTS);\n const durationsSAB = new SharedArrayBuffer(BigUint64Array.BYTES_PER_ELEMENT * maxCycles);\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).slice(0, count).sort(cmp);\n\n const report = reportTypes.map<[string, unknown]>((type) => [type, createReport(durations, type)] as [ReportType, Report]).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;AASjC,OAAO,MAAMC,iBAAiB,CAA6C,EACzEC,OAAO,EACPC,YAAY,EACZC,SAAS,EACTC,SAAS,EACTC,YAAY,EACZC,YAAY,EACZC,WAAW,EACkB;IAC7B,MAAMC,WAAWb,MAAoC,OAAO,EAAEc,KAAK,EAAEC,QAAQ,EAAEC,GAAG,EAAEC,GAAG,EAAEC,IAAI,EAAEC,IAAI,EAAE;QACnG,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,kBAAkBC,WAAWC,iBAAiB,GAAG3B;QACxE,MAAM4B,eAAe,IAAIH,kBAAkBI,eAAeF,iBAAiB,GAAGrB;QAE9E,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,cAAca,KAAK,CAAC,GAAGH,OAAOI,IAAI,CAACxC;QAExE,MAAMyC,SAASjC,YAAYkC,GAAG,CAAoB,CAACC,OAAS;gBAACA;gBAAM5C,aAAauC,WAAWK;aAAM,EAA0BC,MAAM,CAAC;YAAC;gBAAC;gBAASR;aAAM;SAAC;QACpJ,OAAOS,OAAOC,WAAW,CAACL;IAC5B,GAAGvC;IAEHO,SAASsC,KAAK,CAAC,CAACC;QACdC,QAAQF,KAAK,CAACC;IAChB;IAEA,OAAOvC;AACT,EAAE"}
|
package/build/index.cjs
ADDED
|
@@ -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"}
|
package/build/index.d.ts
ADDED
package/build/index.js
ADDED
|
@@ -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,iBAAiB"}
|
package/build/queue.cjs
ADDED
|
@@ -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>(worker: (task: T) => Promise<void>, concurency: number = 1) => {\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,CAAIC,QAAoCC,aAAqB,CAAC;IACvF,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,KAAO,GACbC,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"}
|
package/build/queue.d.ts
ADDED
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>(worker: (task: T) => Promise<void>, concurency: number = 1) => {\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,CAAIC,QAAoCC,aAAqB,CAAC;IACvF,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,KAAO,GACbC,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 valueOf() {\n return Number(div(this.value, this.scale));\n }\n toString() {\n const uncertainty = this.uncertainty ? ` ± ${smartFixed(this.uncertainty)}%` : '';\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(type, 0n);\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 ? (durations[mid - 1] + durations[mid]) / 2n : 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 ? Number(((gap / 2n) * 100n) / modeVal) : 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 ? Math.ceil(-Math.log10(raw)) : 0;\n\n const exp = raw > 100 ? 0 : 2 + extra;\n\n const scale = 10n ** BigInt(exp);\n\n const value = avgNs > 0n ? (nsPerSec * scale) / avgNs : 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":";;;;;;;;;;;IAmBaA,MAAM;eAANA;;IAgCAC,YAAY;eAAZA;;;0BAnDkB;AAG/B,MAAMC,QAAQ;IACZ;QAAEC,MAAM;QAAMC,QAAQ;IAAE;IACxB;QAAED,MAAM;QAAMC,QAAQ;IAAI;IAC1B;QAAED,MAAM;QAAMC,QAAQ;IAAI;IAC1B;QAAED,MAAM;QAAKC,QAAQ;IAAI;IACzB;QAAED,MAAM;QAAKC,QAAQ,KAAK;IAAI;IAC9B;QAAED,MAAM;QAAKC,QAAQ,OAAO;IAAI;CACjC;AAED,SAASC,WAAWC,CAAS;IAC3B,OAAOA,EAAEC,cAAc,CAACC,WAAW;QACjCC,uBAAuB;QACvBC,uBAAuB;QACvBC,aAAa;IACf;AACF;AACO,MAAMX;;;;;IACXY,YACE,AAAgBC,IAAgB,EAChC,AAAgBC,KAAa,EAC7B,AAAgBC,cAAsB,CAAC,EACvC,AAAgBC,QAAgB,EAAE,CAClC;aAJgBH,OAAAA;aACAC,QAAAA;aACAC,cAAAA;aACAC,QAAAA;IACf;IACHC,UAAU;QACR,OAAOC,OAAOC,IAAAA,aAAG,EAAC,IAAI,CAACL,KAAK,EAAE,IAAI,CAACE,KAAK;IAC1C;IACAI,WAAW;QACT,MAAML,cAAc,IAAI,CAACA,WAAW,GAAG,CAAC,GAAG,EAAEV,WAAW,IAAI,CAACU,WAAW,EAAE,CAAC,CAAC,GAAG;QAE/E,MAAMD,QAAQ,IAAI,CAACG,OAAO;QAC1B,IAAI,IAAI,CAACJ,IAAI,KAAK,OAAO;YACvB,OAAO,GAAGR,WAAWS,OAAO,MAAM,EAAEC,aAAa;QACnD;QACA,IAAIM,UAAUP;QACd,IAAIX,OAAO;QAEX,KAAK,MAAM,EAAEA,MAAMmB,CAAC,EAAElB,MAAM,EAAE,IAAIF,MAAO;YACvC,MAAMqB,YAAYT,QAAQV;YAC1B,IAAImB,YAAY,MAAM;gBACpBF,UAAUE;gBACVpB,OAAOmB;gBACP;YACF;QACF;QACA,OAAO,GAAGjB,WAAWgB,SAAS,CAAC,EAAElB,OAAOY,aAAa;IACvD;AACF;AAEO,MAAMd,eAAe,CAACuB,WAA2BX;IACtD,MAAMP,IAAIkB,UAAUC,MAAM;IAC1B,IAAInB,MAAM,GAAG;QACX,OAAO,IAAIN,OAAOa,MAAM,EAAE;IAC5B;IACA,OAAQA;QACN,KAAK;YAAO;gBACV,OAAO,IAAIb,OAAOa,MAAMW,SAAS,CAAC,EAAE;YACtC;QACA,KAAK;YAAO;gBACV,OAAO,IAAIxB,OAAOa,MAAMW,SAAS,CAAClB,IAAI,EAAE;YAC1C;QACA,KAAK;YAAU;gBACb,MAAMoB,MAAMC,KAAKC,KAAK,CAACtB,IAAI;gBAC3B,MAAMuB,MAAMvB,IAAI,MAAM,IAAI,AAACkB,CAAAA,SAAS,CAACE,MAAM,EAAE,GAAGF,SAAS,CAACE,IAAI,AAAD,IAAK,EAAE,GAAGF,SAAS,CAACE,IAAI;gBACrF,OAAO,IAAI1B,OAAOa,MAAMgB;YAC1B;QAEA,KAAK;YAAQ;gBACX,MAAMC,OAAO,IAAIC;gBACjB,IAAIC,WAAW,EAAE;gBACjB,IAAIC,UAAUT,SAAS,CAAC,EAAE;gBAC1B,KAAK,MAAMU,KAAKV,UAAW;oBACzB,MAAMW,QAAQ,AAACL,CAAAA,KAAKM,GAAG,CAACF,MAAM,EAAE,AAAD,IAAK,EAAE;oBACtCJ,KAAKO,GAAG,CAACH,GAAGC;oBACZ,IAAIA,QAAQH,UAAU;wBACpBA,WAAWG;wBACXF,UAAUC;oBACZ;gBACF;gBACA,IAAII,QAAQL;gBACZ,IAAIM,QAAQN;gBACZ,MAAMO,WAAWhB,UAAUiB,OAAO,CAACR;gBACnC,MAAMS,UAAUlB,UAAUmB,WAAW,CAACV;gBACtC,IAAIO,WAAW,GAAGF,QAAQd,SAAS,CAACgB,WAAW,EAAE;gBACjD,IAAIE,UAAUpC,IAAI,GAAGiC,QAAQf,SAAS,CAACkB,UAAU,EAAE;gBACnD,MAAME,MAAMC,IAAAA,aAAG,EAACZ,UAAUK,OAAOC,QAAQN;gBACzC,MAAMlB,cAAckB,UAAU,IAAIf,OAAO,AAAE0B,MAAM,EAAE,GAAI,IAAI,GAAIX,WAAW;gBAC1E,OAAO,IAAIjC,OAAOa,MAAMoB,SAASlB;YACnC;QAEA,KAAK;YAAO;gBACV,IAAI+B,MAAM,EAAE;gBACZ,KAAK,MAAMC,YAAYvB,UAAW;oBAChCsB,OAAOC;gBACT;gBACA,MAAMC,QAAQF,MAAMG,OAAO3C;gBAC3B,MAAM4C,WAAW,cAAc;gBAC/B,MAAMC,MAAMjC,OAAOgC,YAAYhC,OAAO8B;gBACtC,MAAMI,QAAQD,MAAM,IAAIxB,KAAK0B,IAAI,CAAC,CAAC1B,KAAK2B,KAAK,CAACH,QAAQ;gBAEtD,MAAMI,MAAMJ,MAAM,MAAM,IAAI,IAAIC;gBAEhC,MAAMpC,QAAQ,GAAG,IAAIiC,OAAOM;gBAE5B,MAAMzC,QAAQkC,QAAQ,EAAE,GAAG,AAACE,WAAWlC,QAASgC,QAAQ,EAAE;gBAC1D,MAAMQ,YAAYhC,SAAS,CAAClB,IAAI,EAAE,GAAGkB,SAAS,CAAC,EAAE;gBACjD,MAAMT,cAAciC,QAAQ,IAAI9B,OAAOC,IAAAA,aAAG,EAACqC,YAAYxC,OAAO,EAAE,GAAGgC,UAAU;gBAC7E,OAAO,IAAIhD,OAAOa,MAAMC,OAAOC,aAAaC;YAC9C;QACA,KAAK;YAAQ;gBACX,IAAI8B,MAAM,EAAE;gBACZ,KAAK,MAAMC,YAAYvB,UAAW;oBAChCsB,OAAOC;gBACT;gBACA,MAAMjC,QAAQ2C,IAAAA,cAAI,EAACX,KAAKG,OAAO3C,IAAI,EAAE;gBACrC,OAAO,IAAIN,OAAOa,MAAMC;YAC1B;QAEA;YAAS;gBACP,MAAM4C,IAAIxC,OAAOL,KAAK8C,KAAK,CAAC;gBAC5B,IAAID,MAAM,GAAG;oBACX,OAAO,IAAI1D,OAAOa,MAAMW,SAAS,CAAC,EAAE;gBACtC;gBACA,IAAIkC,MAAM,KAAK;oBACb,OAAO,IAAI1D,OAAOa,MAAMW,SAAS,CAAClB,IAAI,EAAE;gBAC1C;gBACA,MAAMsD,MAAMjC,KAAK0B,IAAI,CAAC,AAACK,IAAI,MAAOpD,KAAK;gBACvC,MAAMQ,QAAQU,SAAS,CAACG,KAAKkC,GAAG,CAAClC,KAAKkB,GAAG,CAACe,KAAK,IAAItD,IAAI,GAAG;gBAC1D,MAAMwD,OAAOF,MAAM,IAAIpC,SAAS,CAACoC,MAAM,EAAE,GAAG9C;gBAC5C,MAAMiD,OAAOH,MAAMtD,IAAI,IAAIkB,SAAS,CAACoC,MAAM,EAAE,GAAG9C;gBAChD,MAAM8B,MAAMC,IAAAA,aAAG,EAAC/B,QAAQgD,MAAMC,OAAOjD;gBACrC,MAAMC,cAAcD,QAAQ,IAAII,OAAOC,IAAAA,aAAG,EAACsC,IAAAA,cAAI,EAACb,KAAK,EAAE,EAAE,OAAO,GAAG9B,UAAU,MAAM;gBAEnF,OAAO,IAAId,OAAOa,MAAMC,OAAOC;YACjC;IACF;AACF"}
|
|
@@ -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 valueOf() {\n return Number(div(this.value, this.scale));\n }\n toString() {\n const uncertainty = this.uncertainty ? ` ± ${smartFixed(this.uncertainty)}%` : '';\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(type, 0n);\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 ? (durations[mid - 1] + durations[mid]) / 2n : 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 ? Number(((gap / 2n) * 100n) / modeVal) : 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 ? Math.ceil(-Math.log10(raw)) : 0;\n\n const exp = raw > 100 ? 0 : 2 + extra;\n\n const scale = 10n ** BigInt(exp);\n\n const value = avgNs > 0n ? (nsPerSec * scale) / avgNs : 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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"}
|