@ac-bench/util 0.1.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.
- package/LICENSE +28 -0
- package/README.md +36 -0
- package/dist/event-loop-load.d.ts +17 -0
- package/dist/event-loop-load.d.ts.map +1 -0
- package/dist/event-loop-load.js +40 -0
- package/dist/event-loop-load.js.map +1 -0
- package/dist/fixtures.d.ts +57 -0
- package/dist/fixtures.d.ts.map +1 -0
- package/dist/fixtures.js +104 -0
- package/dist/fixtures.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/native-toolchain.d.ts +70 -0
- package/dist/native-toolchain.d.ts.map +1 -0
- package/dist/native-toolchain.js +116 -0
- package/dist/native-toolchain.js.map +1 -0
- package/package.json +64 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-2026 Anthony Champagne and contributors
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/README.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# @ac-bench/util
|
|
2
|
+
|
|
3
|
+
The fixtures a benchmark needs before it can measure anything honestly.
|
|
4
|
+
|
|
5
|
+
Two things go wrong most often. Inputs generated with `Math.random()` differ
|
|
6
|
+
between runs, so two runs are not comparable; and an "optimised" implementation
|
|
7
|
+
that quietly returns the wrong answer wins every benchmark. These helpers
|
|
8
|
+
address both: every generator is seeded, and `checksum` gives you a cheap way to
|
|
9
|
+
assert two implementations agreed.
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { checksum, DEFAULT_SEED, randomFloat64Values } from "@ac-bench/util";
|
|
13
|
+
|
|
14
|
+
// Same values on every run and every machine.
|
|
15
|
+
const values = randomFloat64Values(10_000, DEFAULT_SEED);
|
|
16
|
+
|
|
17
|
+
const expected = checksum(reference(values));
|
|
18
|
+
// ... assert each candidate reproduces `expected` before trusting its timing.
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## What it exposes
|
|
22
|
+
|
|
23
|
+
- **Seeded workloads** — `randomFloat64Values`, `randomUint32Values`,
|
|
24
|
+
`randomInts`, `words`, `skewedIndices`, `distinctKeys`, and `DEFAULT_SEED`.
|
|
25
|
+
- **Equivalence** — `checksum`, `sequentialChecksum`, `hashString`,
|
|
26
|
+
`stringHash32`: order-sensitive digests for proving two implementations
|
|
27
|
+
produced the same output.
|
|
28
|
+
- **Contention** — `EventLoopLoad`, a controllable background load, for
|
|
29
|
+
measuring the same operation idle and under pressure.
|
|
30
|
+
- **Native toolchains** — `prepareNativeToolchains`, for suites comparing
|
|
31
|
+
against a reference implementation in another language.
|
|
32
|
+
|
|
33
|
+
Generic process helpers (`execAsync`, `hasBinary`, `createTempDir`) are
|
|
34
|
+
`@ac-kit/node`'s — they carry no measurement-specific semantics. Spawn-overhead
|
|
35
|
+
calibration lives with the measure that needs it, in
|
|
36
|
+
`@ac-bench/measure-duration`.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keeps the event loop busy until stopped.
|
|
3
|
+
*
|
|
4
|
+
* Timer benchmarks run on an idle loop measure a best case nobody experiences:
|
|
5
|
+
* the loop only checks its timer list once per iteration, so lateness is
|
|
6
|
+
* bounded below by whatever else is already queued. Measuring a condition both
|
|
7
|
+
* idle and loaded, and comparing the two, shows how much of the result belongs
|
|
8
|
+
* to the runtime rather than to the timer.
|
|
9
|
+
*/
|
|
10
|
+
export declare class EventLoopLoad {
|
|
11
|
+
private running;
|
|
12
|
+
private handle;
|
|
13
|
+
start(): void;
|
|
14
|
+
stop(): void;
|
|
15
|
+
private schedule;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=event-loop-load.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-loop-load.d.ts","sourceRoot":"","sources":["../src/event-loop-load.ts"],"names":[],"mappings":"AAKA;;;;;;;;GAQG;AACH,qBAAa,aAAa;IACzB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAiC;IAE/C,KAAK,IAAI,IAAI,CAMZ;IAED,IAAI,IAAI,IAAI,CAMX;IAED,OAAO,CAAC,QAAQ;CAShB"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { busyWaitSync } from "@ac-kit/test-util";
|
|
2
|
+
/** Milliseconds of CPU burned per scheduled slice. */
|
|
3
|
+
const SLICE_MS = 2;
|
|
4
|
+
/**
|
|
5
|
+
* Keeps the event loop busy until stopped.
|
|
6
|
+
*
|
|
7
|
+
* Timer benchmarks run on an idle loop measure a best case nobody experiences:
|
|
8
|
+
* the loop only checks its timer list once per iteration, so lateness is
|
|
9
|
+
* bounded below by whatever else is already queued. Measuring a condition both
|
|
10
|
+
* idle and loaded, and comparing the two, shows how much of the result belongs
|
|
11
|
+
* to the runtime rather than to the timer.
|
|
12
|
+
*/
|
|
13
|
+
export class EventLoopLoad {
|
|
14
|
+
running = false;
|
|
15
|
+
handle = null;
|
|
16
|
+
start() {
|
|
17
|
+
if (this.running) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
this.running = true;
|
|
21
|
+
this.schedule();
|
|
22
|
+
}
|
|
23
|
+
stop() {
|
|
24
|
+
this.running = false;
|
|
25
|
+
if (this.handle) {
|
|
26
|
+
clearImmediate(this.handle);
|
|
27
|
+
this.handle = null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
schedule() {
|
|
31
|
+
if (!this.running) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
this.handle = setImmediate(() => {
|
|
35
|
+
busyWaitSync(SLICE_MS);
|
|
36
|
+
this.schedule();
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=event-loop-load.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-loop-load.js","sourceRoot":"","sources":["../src/event-loop-load.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,sDAAsD;AACtD,MAAM,QAAQ,GAAG,CAAC,CAAC;AAEnB;;;;;;;;GAQG;AACH,MAAM,OAAO,aAAa;IACjB,OAAO,GAAG,KAAK,CAAC;IAChB,MAAM,GAA4B,IAAI,CAAC;IAE/C,KAAK;QACJ,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAClB,OAAO;QACR,CAAC;QACD,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,CAAC,QAAQ,EAAE,CAAC;IACjB,CAAC;IAED,IAAI;QACH,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QACrB,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACjB,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC5B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACpB,CAAC;IACF,CAAC;IAEO,QAAQ;QACf,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YACnB,OAAO;QACR,CAAC;QACD,IAAI,CAAC,MAAM,GAAG,YAAY,CAAC,GAAG,EAAE;YAC/B,YAAY,CAAC,QAAQ,CAAC,CAAC;YACvB,IAAI,CAAC,QAAQ,EAAE,CAAC;QACjB,CAAC,CAAC,CAAC;IACJ,CAAC;CACD"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fixtures shared by every benchmark package.
|
|
3
|
+
*
|
|
4
|
+
* Contenders inside a group must see identical input and produce the same
|
|
5
|
+
* answer; otherwise the ranking measures the fixture rather than the code. The
|
|
6
|
+
* generators here are deterministic so a run can be reproduced, and the
|
|
7
|
+
* reference programs in other languages can generate the same values.
|
|
8
|
+
*/
|
|
9
|
+
/** Default seed, shared so unrelated packages produce comparable fixtures. */
|
|
10
|
+
export declare const DEFAULT_SEED = 625341585;
|
|
11
|
+
/**
|
|
12
|
+
* `count` pseudo-random unsigned 32-bit values.
|
|
13
|
+
*
|
|
14
|
+
* Recovers the exact 32-bit state {@link xorshift32} divides down to a float:
|
|
15
|
+
* multiplying back by 2^32 is lossless for a value that started as an integer
|
|
16
|
+
* with 32 significant bits or fewer.
|
|
17
|
+
*/
|
|
18
|
+
export declare function randomUint32Values(count: number, seed?: number): number[];
|
|
19
|
+
/** `count` pseudo-random floats in `[0, 1)`. */
|
|
20
|
+
export declare function randomFloat64Values(count: number, seed?: number): number[];
|
|
21
|
+
/** Sum of `values`, truncated to 32 bits. */
|
|
22
|
+
export declare function checksum(values: Iterable<number>): number;
|
|
23
|
+
/** Sum of `0..count-1`, the answer every FIFO workload must reach. */
|
|
24
|
+
export declare function sequentialChecksum(count: number): number;
|
|
25
|
+
/** Cheap order-sensitive hash of a string, used to verify text workloads. */
|
|
26
|
+
export declare function hashString(text: string): number;
|
|
27
|
+
/**
|
|
28
|
+
* Seeded 32-bit hash of a string, for structures that take a hash function as a
|
|
29
|
+
* parameter (Bloom filters, Count-Min sketches, HyperLogLog, …).
|
|
30
|
+
*
|
|
31
|
+
* Distinct from {@link hashString}, which is an unseeded checksum used to
|
|
32
|
+
* _verify_ a workload. This one is an input to the code under test, so it has
|
|
33
|
+
* to avalanche: the error bounds those structures document assume a uniformly
|
|
34
|
+
* distributed hash, and a weak one would make a suite measure the hash instead
|
|
35
|
+
* of the structure.
|
|
36
|
+
*
|
|
37
|
+
* Its shape matches `@ac-kit/data`'s `Hash32<string>` structurally, so it can
|
|
38
|
+
* be passed straight in without this package depending on `@ac-kit/data`.
|
|
39
|
+
*/
|
|
40
|
+
export declare function stringHash32(text: string, seed: number): number;
|
|
41
|
+
/** `count` distinct keys, deterministic so every run compares like with like. */
|
|
42
|
+
export declare function distinctKeys(count: number, prefix?: string): string[];
|
|
43
|
+
/** `count` pseudo-random integers in `[0, bound)`. */
|
|
44
|
+
export declare function randomInts(count: number, bound: number, seed?: number): number[];
|
|
45
|
+
/**
|
|
46
|
+
* A skewed access pattern over `[0, universe)`: low indices drawn far more
|
|
47
|
+
* often than high ones.
|
|
48
|
+
*
|
|
49
|
+
* A uniform pattern makes every cache look alike, because a replacement policy
|
|
50
|
+
* only shows itself when some entries are genuinely hotter than others.
|
|
51
|
+
* Squaring a uniform draw is the cheapest way to get that skew and stays
|
|
52
|
+
* reproducible.
|
|
53
|
+
*/
|
|
54
|
+
export declare function skewedIndices(count: number, universe: number, seed?: number): number[];
|
|
55
|
+
/** `count` short lowercase words, deterministic. */
|
|
56
|
+
export declare function words(count: number, seed?: number): string[];
|
|
57
|
+
//# sourceMappingURL=fixtures.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fixtures.d.ts","sourceRoot":"","sources":["../src/fixtures.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,8EAA8E;AAC9E,eAAO,MAAM,YAAY,YAAa,CAAC;AAEvC;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CACjC,KAAK,EAAE,MAAM,EACb,IAAI,SAAe,GACjB,MAAM,EAAE,CAKV;AAED,gDAAgD;AAChD,wBAAgB,mBAAmB,CAClC,KAAK,EAAE,MAAM,EACb,IAAI,SAAe,GACjB,MAAM,EAAE,CAGV;AAED,6CAA6C;AAC7C,wBAAgB,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,MAAM,CAMzD;AAED,sEAAsE;AACtE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMxD;AAED,6EAA6E;AAC7E,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED,iFAAiF;AACjF,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,SAAQ,GAAG,MAAM,EAAE,CAEpE;AAED,sDAAsD;AACtD,wBAAgB,UAAU,CACzB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,IAAI,SAAe,GACjB,MAAM,EAAE,CAGV;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC5B,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,IAAI,SAAe,GACjB,MAAM,EAAE,CAMV;AAED,oDAAoD;AACpD,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,SAAe,GAAG,MAAM,EAAE,CAclE"}
|
package/dist/fixtures.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fixtures shared by every benchmark package.
|
|
3
|
+
*
|
|
4
|
+
* Contenders inside a group must see identical input and produce the same
|
|
5
|
+
* answer; otherwise the ranking measures the fixture rather than the code. The
|
|
6
|
+
* generators here are deterministic so a run can be reproduced, and the
|
|
7
|
+
* reference programs in other languages can generate the same values.
|
|
8
|
+
*/
|
|
9
|
+
import { encodeTextUtf8 } from "@ac-kit/core";
|
|
10
|
+
import { xorshift32 } from "@ac-kit/math-random";
|
|
11
|
+
import { fnv1a_32, murmur3_32 } from "@ac-kit/noncrypto-hash";
|
|
12
|
+
/** Default seed, shared so unrelated packages produce comparable fixtures. */
|
|
13
|
+
export const DEFAULT_SEED = 0x2545f491;
|
|
14
|
+
/**
|
|
15
|
+
* `count` pseudo-random unsigned 32-bit values.
|
|
16
|
+
*
|
|
17
|
+
* Recovers the exact 32-bit state {@link xorshift32} divides down to a float:
|
|
18
|
+
* multiplying back by 2^32 is lossless for a value that started as an integer
|
|
19
|
+
* with 32 significant bits or fewer.
|
|
20
|
+
*/
|
|
21
|
+
export function randomUint32Values(count, seed = DEFAULT_SEED) {
|
|
22
|
+
const rand = xorshift32(seed);
|
|
23
|
+
return Array.from({ length: count }, () => Math.round(rand() * 4294967296));
|
|
24
|
+
}
|
|
25
|
+
/** `count` pseudo-random floats in `[0, 1)`. */
|
|
26
|
+
export function randomFloat64Values(count, seed = DEFAULT_SEED) {
|
|
27
|
+
const rand = xorshift32(seed);
|
|
28
|
+
return Array.from({ length: count }, () => rand());
|
|
29
|
+
}
|
|
30
|
+
/** Sum of `values`, truncated to 32 bits. */
|
|
31
|
+
export function checksum(values) {
|
|
32
|
+
let sum = 0;
|
|
33
|
+
for (const value of values) {
|
|
34
|
+
sum = (sum + value) >>> 0;
|
|
35
|
+
}
|
|
36
|
+
return sum;
|
|
37
|
+
}
|
|
38
|
+
/** Sum of `0..count-1`, the answer every FIFO workload must reach. */
|
|
39
|
+
export function sequentialChecksum(count) {
|
|
40
|
+
let sum = 0;
|
|
41
|
+
for (let index = 0; index < count; index++) {
|
|
42
|
+
sum = (sum + index) >>> 0;
|
|
43
|
+
}
|
|
44
|
+
return sum;
|
|
45
|
+
}
|
|
46
|
+
/** Cheap order-sensitive hash of a string, used to verify text workloads. */
|
|
47
|
+
export function hashString(text) {
|
|
48
|
+
return fnv1a_32(encodeTextUtf8(text));
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Seeded 32-bit hash of a string, for structures that take a hash function as a
|
|
52
|
+
* parameter (Bloom filters, Count-Min sketches, HyperLogLog, …).
|
|
53
|
+
*
|
|
54
|
+
* Distinct from {@link hashString}, which is an unseeded checksum used to
|
|
55
|
+
* _verify_ a workload. This one is an input to the code under test, so it has
|
|
56
|
+
* to avalanche: the error bounds those structures document assume a uniformly
|
|
57
|
+
* distributed hash, and a weak one would make a suite measure the hash instead
|
|
58
|
+
* of the structure.
|
|
59
|
+
*
|
|
60
|
+
* Its shape matches `@ac-kit/data`'s `Hash32<string>` structurally, so it can
|
|
61
|
+
* be passed straight in without this package depending on `@ac-kit/data`.
|
|
62
|
+
*/
|
|
63
|
+
export function stringHash32(text, seed) {
|
|
64
|
+
return murmur3_32(encodeTextUtf8(text), seed);
|
|
65
|
+
}
|
|
66
|
+
/** `count` distinct keys, deterministic so every run compares like with like. */
|
|
67
|
+
export function distinctKeys(count, prefix = "key") {
|
|
68
|
+
return Array.from({ length: count }, (_, index) => `${prefix}-${index}`);
|
|
69
|
+
}
|
|
70
|
+
/** `count` pseudo-random integers in `[0, bound)`. */
|
|
71
|
+
export function randomInts(count, bound, seed = DEFAULT_SEED) {
|
|
72
|
+
const rand = xorshift32(seed);
|
|
73
|
+
return Array.from({ length: count }, () => Math.floor(rand() * bound));
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* A skewed access pattern over `[0, universe)`: low indices drawn far more
|
|
77
|
+
* often than high ones.
|
|
78
|
+
*
|
|
79
|
+
* A uniform pattern makes every cache look alike, because a replacement policy
|
|
80
|
+
* only shows itself when some entries are genuinely hotter than others.
|
|
81
|
+
* Squaring a uniform draw is the cheapest way to get that skew and stays
|
|
82
|
+
* reproducible.
|
|
83
|
+
*/
|
|
84
|
+
export function skewedIndices(count, universe, seed = DEFAULT_SEED) {
|
|
85
|
+
const rand = xorshift32(seed);
|
|
86
|
+
return Array.from({ length: count }, () => {
|
|
87
|
+
const value = rand();
|
|
88
|
+
return Math.floor(value * value * universe);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/** `count` short lowercase words, deterministic. */
|
|
92
|
+
export function words(count, seed = DEFAULT_SEED) {
|
|
93
|
+
const rand = xorshift32(seed);
|
|
94
|
+
const alphabet = "abcdefghijklmnopqrstuvwxyz";
|
|
95
|
+
return Array.from({ length: count }, () => {
|
|
96
|
+
const length = 3 + Math.floor(rand() * 6);
|
|
97
|
+
let word = "";
|
|
98
|
+
for (let index = 0; index < length; index++) {
|
|
99
|
+
word += alphabet[Math.floor(rand() * alphabet.length)];
|
|
100
|
+
}
|
|
101
|
+
return word;
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=fixtures.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fixtures.js","sourceRoot":"","sources":["../src/fixtures.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detects and, where needed, compiles a runnable reference program per
|
|
3
|
+
* toolchain spec — the machinery shared by every cross-language benchmark
|
|
4
|
+
* fixture in this workspace (a duration comparison is only meaningful once a
|
|
5
|
+
* program to compare against exists and actually runs).
|
|
6
|
+
*/
|
|
7
|
+
import type { ArtifactCache } from "@ac-kit/app-system";
|
|
8
|
+
/** One candidate reference implementation, interpreted or compiled from source. */
|
|
9
|
+
export interface NativeToolchainSpec {
|
|
10
|
+
/** Attribution label carried into `NativeProgram.language`, e.g. `"C++"`. */
|
|
11
|
+
language: string;
|
|
12
|
+
/** Absolute path to the reference program's source file. */
|
|
13
|
+
sourcePath: string;
|
|
14
|
+
/**
|
|
15
|
+
* Binaries tried in order; the first one found on `PATH` is used. For an
|
|
16
|
+
* interpreted toolchain this is the interpreter itself (e.g. `["python3"]`);
|
|
17
|
+
* for a compiled one, the candidate compilers (e.g. `["g++", "clang++"]`).
|
|
18
|
+
*/
|
|
19
|
+
candidates: readonly string[];
|
|
20
|
+
/**
|
|
21
|
+
* Builds the compile command for whichever candidate was found. Omit for an
|
|
22
|
+
* interpreted toolchain, where `sourcePath` is invoked directly as the
|
|
23
|
+
* program's first argument instead.
|
|
24
|
+
*/
|
|
25
|
+
compile?: (compiler: string, sourcePath: string, outBinary: string) => {
|
|
26
|
+
command: string;
|
|
27
|
+
args: readonly string[];
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* A reference program ready to spawn: `command` with `baseArgs` prepended to
|
|
32
|
+
* any call.
|
|
33
|
+
*/
|
|
34
|
+
export interface NativeProgram {
|
|
35
|
+
language: string;
|
|
36
|
+
command: string;
|
|
37
|
+
baseArgs: readonly string[];
|
|
38
|
+
}
|
|
39
|
+
export interface PrepareNativeToolchainsOptions {
|
|
40
|
+
/**
|
|
41
|
+
* Keeps only the programs that pass, e.g. a smoke test comparing against a
|
|
42
|
+
* known result. Every detected/compiled program is kept when omitted.
|
|
43
|
+
*/
|
|
44
|
+
verify?: (program: NativeProgram) => Promise<boolean>;
|
|
45
|
+
/**
|
|
46
|
+
* Where compiled programs are cached. Supply the runner's own cache to share
|
|
47
|
+
* one compile across every process of a run; omitted, a persistent cache
|
|
48
|
+
* under `node_modules/.cache/ac-bench` is used.
|
|
49
|
+
*/
|
|
50
|
+
cache?: ArtifactCache;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Detects, and where a `compile` step is declared compiles, one
|
|
54
|
+
* {@link NativeProgram} per spec whose first matching candidate is found on
|
|
55
|
+
* `PATH` — specs with no candidate found are silently skipped, and a spec whose
|
|
56
|
+
* compile step fails is reported to `stderr` and skipped rather than failing
|
|
57
|
+
* the whole batch, since a benchmark should degrade to comparing fewer
|
|
58
|
+
* languages rather than not comparing at all when one toolchain is missing.
|
|
59
|
+
*
|
|
60
|
+
* Compiled outputs are content-addressed by source text, compiler, compiler
|
|
61
|
+
* version and compile flags, so a program survives across runs and is rebuilt
|
|
62
|
+
* exactly when one of those changes. Nothing is written outside the cache
|
|
63
|
+
* root.
|
|
64
|
+
*
|
|
65
|
+
* Call once per distinct spec set and memoize the result in the caller (a lazy
|
|
66
|
+
* module-level singleton, as every consumer here already does) — the artifact
|
|
67
|
+
* cache spares the compile, not the detection work.
|
|
68
|
+
*/
|
|
69
|
+
export declare function prepareNativeToolchains(specs: readonly NativeToolchainSpec[], options?: PrepareNativeToolchainsOptions): Promise<readonly NativeProgram[]>;
|
|
70
|
+
//# sourceMappingURL=native-toolchain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native-toolchain.d.ts","sourceRoot":"","sources":["../src/native-toolchain.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAe,MAAM,oBAAoB,CAAC;AAKrE,mFAAmF;AACnF,MAAM,WAAW,mBAAmB;IACnC,6EAA6E;IAC7E,QAAQ,EAAE,MAAM,CAAC;IAEjB,4DAA4D;IAC5D,UAAU,EAAE,MAAM,CAAC;IAEnB;;;;OAIG;IACH,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IAE9B;;;;OAIG;IACH,OAAO,CAAC,EAAE,CACT,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,KACb;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,CAAC;CAClD;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,8BAA8B;IAC9C;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAEtD;;;;OAIG;IACH,KAAK,CAAC,EAAE,aAAa,CAAC;CACtB;AAaD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,uBAAuB,CAC5C,KAAK,EAAE,SAAS,mBAAmB,EAAE,EACrC,OAAO,GAAE,8BAAmC,GAC1C,OAAO,CAAC,SAAS,aAAa,EAAE,CAAC,CAiEnC"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detects and, where needed, compiles a runnable reference program per
|
|
3
|
+
* toolchain spec — the machinery shared by every cross-language benchmark
|
|
4
|
+
* fixture in this workspace (a duration comparison is only meaningful once a
|
|
5
|
+
* program to compare against exists and actually runs).
|
|
6
|
+
*/
|
|
7
|
+
import { readFile } from "node:fs/promises";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { createArtifactCache, hasBinary } from "@ac-kit/app-system";
|
|
10
|
+
import { slugify } from "@ac-kit/core";
|
|
11
|
+
import { spawnProcess } from "@ac-kit/node";
|
|
12
|
+
const DEFAULT_CACHE_ROOT = join("node_modules", ".cache", "ac-bench");
|
|
13
|
+
/** Best-effort, for the artifact identity: a compiler upgrade must invalidate. */
|
|
14
|
+
async function probeVersion(compiler) {
|
|
15
|
+
try {
|
|
16
|
+
return (await spawnProcess(compiler, ["--version"])).stdout;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return "";
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Detects, and where a `compile` step is declared compiles, one
|
|
24
|
+
* {@link NativeProgram} per spec whose first matching candidate is found on
|
|
25
|
+
* `PATH` — specs with no candidate found are silently skipped, and a spec whose
|
|
26
|
+
* compile step fails is reported to `stderr` and skipped rather than failing
|
|
27
|
+
* the whole batch, since a benchmark should degrade to comparing fewer
|
|
28
|
+
* languages rather than not comparing at all when one toolchain is missing.
|
|
29
|
+
*
|
|
30
|
+
* Compiled outputs are content-addressed by source text, compiler, compiler
|
|
31
|
+
* version and compile flags, so a program survives across runs and is rebuilt
|
|
32
|
+
* exactly when one of those changes. Nothing is written outside the cache
|
|
33
|
+
* root.
|
|
34
|
+
*
|
|
35
|
+
* Call once per distinct spec set and memoize the result in the caller (a lazy
|
|
36
|
+
* module-level singleton, as every consumer here already does) — the artifact
|
|
37
|
+
* cache spares the compile, not the detection work.
|
|
38
|
+
*/
|
|
39
|
+
export async function prepareNativeToolchains(specs, options = {}) {
|
|
40
|
+
const cache = options.cache ?? createArtifactCache({ root: DEFAULT_CACHE_ROOT });
|
|
41
|
+
const programs = [];
|
|
42
|
+
for (const spec of specs) {
|
|
43
|
+
let found = null;
|
|
44
|
+
for (const candidate of spec.candidates) {
|
|
45
|
+
if (await hasBinary(candidate)) {
|
|
46
|
+
found = candidate;
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (found === null) {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (!spec.compile) {
|
|
54
|
+
programs.push({
|
|
55
|
+
language: spec.language,
|
|
56
|
+
command: found,
|
|
57
|
+
baseArgs: [spec.sourcePath],
|
|
58
|
+
});
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
const compile = spec.compile;
|
|
62
|
+
const binaryName = slugify(spec.language, { fallback: "program" });
|
|
63
|
+
try {
|
|
64
|
+
const directory = await cache.ensure(await buildArtifactKey(spec, compile, found, binaryName), async (staging, signal) => {
|
|
65
|
+
const { command, args } = compile(found, spec.sourcePath, join(staging, binaryName));
|
|
66
|
+
await spawnProcess(command, args, { signal });
|
|
67
|
+
});
|
|
68
|
+
programs.push({
|
|
69
|
+
language: spec.language,
|
|
70
|
+
command: join(directory, binaryName),
|
|
71
|
+
baseArgs: [],
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
process.stderr.write(`${spec.language} reference not built: ${String(error)}\n`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (!options.verify) {
|
|
79
|
+
return programs;
|
|
80
|
+
}
|
|
81
|
+
const usable = [];
|
|
82
|
+
for (const program of programs) {
|
|
83
|
+
if (await options.verify(program)) {
|
|
84
|
+
usable.push(program);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return usable;
|
|
88
|
+
}
|
|
89
|
+
async function buildArtifactKey(spec, compile, compiler, binaryName) {
|
|
90
|
+
// The real output path differs per attempt, so the identity uses a fixed
|
|
91
|
+
// placeholder — otherwise no two calls could ever agree on a key.
|
|
92
|
+
const { command, args } = compile(compiler, spec.sourcePath, join("<artifact>", binaryName));
|
|
93
|
+
return {
|
|
94
|
+
name: spec.language,
|
|
95
|
+
inputs: [
|
|
96
|
+
await readSourceOrPath(spec.sourcePath),
|
|
97
|
+
compiler,
|
|
98
|
+
await probeVersion(compiler),
|
|
99
|
+
command,
|
|
100
|
+
...args,
|
|
101
|
+
],
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* An unreadable source still gets an identity: the compile is about to fail on
|
|
106
|
+
* it anyway, and a failed build is never cached.
|
|
107
|
+
*/
|
|
108
|
+
async function readSourceOrPath(sourcePath) {
|
|
109
|
+
try {
|
|
110
|
+
return await readFile(sourcePath, "utf8");
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return `\u0000unreadable:${sourcePath}`;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=native-toolchain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native-toolchain.js","sourceRoot":"","sources":["../src/native-toolchain.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ac-bench/util",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Helpers for writing benchmarks: deterministic PRNG workloads, an order-sensitive output hash, an equivalence guard, a controllable event-loop load, and spawn-overhead measurement.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"benchmark",
|
|
7
|
+
"fixture",
|
|
8
|
+
"prng",
|
|
9
|
+
"workload"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://github.com/anthochamp/node-essentials/tree/main/bench/util#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/anthochamp/node-essentials/issues"
|
|
14
|
+
},
|
|
15
|
+
"license": "BSD-3-Clause",
|
|
16
|
+
"author": "Anthony Champagne <dev@anthonychampagne.fr>",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/anthochamp/node-essentials.git",
|
|
20
|
+
"directory": "bench/util"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist"
|
|
24
|
+
],
|
|
25
|
+
"type": "module",
|
|
26
|
+
"sideEffects": false,
|
|
27
|
+
"main": "./dist/index.js",
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"main": "./dist/index.js",
|
|
30
|
+
"access": "public"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"compile": "tsc -p ./tsconfig.build.json --outDir dist",
|
|
34
|
+
"compile:check": "tsc -noEmit -p ./tsconfig.json",
|
|
35
|
+
"clean": "rimraf dist",
|
|
36
|
+
"build": "yarn clean && yarn compile",
|
|
37
|
+
"test": "yarn run -T vitest run",
|
|
38
|
+
"prepack": "yarn build"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@ac-kit/app-system": "^0.1.0",
|
|
42
|
+
"@ac-kit/core": "^0.1.0",
|
|
43
|
+
"@ac-kit/math-random": "^0.1.0",
|
|
44
|
+
"@ac-kit/math-stats": "^0.1.0",
|
|
45
|
+
"@ac-kit/node": "^0.1.0",
|
|
46
|
+
"@ac-kit/noncrypto-hash": "^0.1.0",
|
|
47
|
+
"@ac-kit/test-util": "^0.1.0"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@ac-kit/tsconfig": "0.1.0",
|
|
51
|
+
"@ac-kit/typedoc-config": "0.1.0",
|
|
52
|
+
"@ac-kit/vitest-config": "0.1.0",
|
|
53
|
+
"@types/node": "26.5.1",
|
|
54
|
+
"@typescript/native": "typescript@7.0.2",
|
|
55
|
+
"rimraf": "6.1.3",
|
|
56
|
+
"typedoc": "0.28.20",
|
|
57
|
+
"typescript": "@typescript/typescript6@6.0.2",
|
|
58
|
+
"vite": "8.3.0",
|
|
59
|
+
"vitest": "5.0.0"
|
|
60
|
+
},
|
|
61
|
+
"engines": {
|
|
62
|
+
"node": ">=24"
|
|
63
|
+
}
|
|
64
|
+
}
|