@ac-bench/measure-duration 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.
Files changed (79) hide show
  1. package/LICENSE +28 -0
  2. package/README.md +34 -0
  3. package/dist/_harness-overhead.d.ts +36 -0
  4. package/dist/_harness-overhead.d.ts.map +1 -0
  5. package/dist/_harness-overhead.js +62 -0
  6. package/dist/_harness-overhead.js.map +1 -0
  7. package/dist/_iteration-protocol.d.ts +18 -0
  8. package/dist/_iteration-protocol.d.ts.map +1 -0
  9. package/dist/_iteration-protocol.js +17 -0
  10. package/dist/_iteration-protocol.js.map +1 -0
  11. package/dist/_iteration-source.d.ts +64 -0
  12. package/dist/_iteration-source.d.ts.map +1 -0
  13. package/dist/_iteration-source.js +135 -0
  14. package/dist/_iteration-source.js.map +1 -0
  15. package/dist/_pool.d.ts +48 -0
  16. package/dist/_pool.d.ts.map +1 -0
  17. package/dist/_pool.js +219 -0
  18. package/dist/_pool.js.map +1 -0
  19. package/dist/_process-execution.d.ts +62 -0
  20. package/dist/_process-execution.d.ts.map +1 -0
  21. package/dist/_process-execution.js +142 -0
  22. package/dist/_process-execution.js.map +1 -0
  23. package/dist/_run-case.d.ts +32 -0
  24. package/dist/_run-case.d.ts.map +1 -0
  25. package/dist/_run-case.js +267 -0
  26. package/dist/_run-case.js.map +1 -0
  27. package/dist/_sampling-options.d.ts +61 -0
  28. package/dist/_sampling-options.d.ts.map +1 -0
  29. package/dist/_sampling-options.js +19 -0
  30. package/dist/_sampling-options.js.map +1 -0
  31. package/dist/_sampling-report.d.ts +44 -0
  32. package/dist/_sampling-report.d.ts.map +1 -0
  33. package/dist/_sampling-report.js +43 -0
  34. package/dist/_sampling-report.js.map +1 -0
  35. package/dist/_spawn-baseline.d.ts +35 -0
  36. package/dist/_spawn-baseline.d.ts.map +1 -0
  37. package/dist/_spawn-baseline.js +22 -0
  38. package/dist/_spawn-baseline.js.map +1 -0
  39. package/dist/_statistics-schema.d.ts +28 -0
  40. package/dist/_statistics-schema.d.ts.map +1 -0
  41. package/dist/_statistics-schema.js +59 -0
  42. package/dist/_statistics-schema.js.map +1 -0
  43. package/dist/_statistics.d.ts +25 -0
  44. package/dist/_statistics.d.ts.map +1 -0
  45. package/dist/_statistics.js +72 -0
  46. package/dist/_statistics.js.map +1 -0
  47. package/dist/_types.d.ts +302 -0
  48. package/dist/_types.d.ts.map +1 -0
  49. package/dist/_types.js +57 -0
  50. package/dist/_types.js.map +1 -0
  51. package/dist/_warnings.d.ts +62 -0
  52. package/dist/_warnings.d.ts.map +1 -0
  53. package/dist/_warnings.js +132 -0
  54. package/dist/_warnings.js.map +1 -0
  55. package/dist/decl.d.ts +21 -0
  56. package/dist/decl.d.ts.map +1 -0
  57. package/dist/decl.js +54 -0
  58. package/dist/decl.js.map +1 -0
  59. package/dist/index.d.ts +5 -0
  60. package/dist/index.d.ts.map +1 -0
  61. package/dist/index.js +5 -0
  62. package/dist/index.js.map +1 -0
  63. package/dist/iteration-child.d.ts +2 -0
  64. package/dist/iteration-child.d.ts.map +1 -0
  65. package/dist/iteration-child.js +37 -0
  66. package/dist/iteration-child.js.map +1 -0
  67. package/dist/options.d.ts +59 -0
  68. package/dist/options.d.ts.map +1 -0
  69. package/dist/options.js +2 -0
  70. package/dist/options.js.map +1 -0
  71. package/dist/plugin.d.ts +3 -0
  72. package/dist/plugin.d.ts.map +1 -0
  73. package/dist/plugin.js +205 -0
  74. package/dist/plugin.js.map +1 -0
  75. package/dist/spawn-overhead.d.ts +59 -0
  76. package/dist/spawn-overhead.d.ts.map +1 -0
  77. package/dist/spawn-overhead.js +80 -0
  78. package/dist/spawn-overhead.js.map +1 -0
  79. package/package.json +77 -0
@@ -0,0 +1,59 @@
1
+ import { DurationStatistics } from "./_types.js";
2
+ export interface SpawnOverheadOptions {
3
+ /** Unmeasured invocations run first. Defaults to 3. */
4
+ warmup?: number;
5
+ /** Measured invocations. Defaults to 20. */
6
+ runs?: number;
7
+ }
8
+ /**
9
+ * The do-nothing equivalent of a command being benchmarked.
10
+ *
11
+ * To subtract spawn cost honestly, this must start the same binary and do the
12
+ * same unavoidable setup as the real command, minus the work under test. For
13
+ * `python3 -c "<protocol code>"` the right baseline is `python3 -c "import
14
+ * smtplib"`, not `python3 --version`: interpreter startup is only part of the
15
+ * cost, module import is the rest.
16
+ */
17
+ export interface SpawnBaseline {
18
+ /** Label used in reports, and matched against a case's `tags.language`. */
19
+ name: string;
20
+ /** Executable to run. Resolved on `PATH`; never passed through a shell. */
21
+ command: string;
22
+ /** Arguments producing the cheapest meaningful invocation. */
23
+ args?: readonly string[];
24
+ }
25
+ /**
26
+ * Measure how long it takes to start and stop a command that does nothing.
27
+ *
28
+ * A benchmark that spawns a process per iteration includes this cost in every
29
+ * sample, while an in-process contender pays none of it. Measuring it makes the
30
+ * bias visible and lets reports show an adjusted figure.
31
+ *
32
+ * @param command Executable to run. Resolved on `PATH`; never passed through a
33
+ * shell.
34
+ * @param args Arguments producing the cheapest meaningful invocation.
35
+ * @param options Sampling policy.
36
+ * @returns Statistics over the measured invocations.
37
+ * @throws {Error} If the command cannot be executed at all.
38
+ */
39
+ export declare function measureSpawnOverhead(command: string, args: readonly string[], options?: SpawnOverheadOptions): Promise<DurationStatistics>;
40
+ /**
41
+ * Floor cost of spawning any process from this runtime.
42
+ *
43
+ * Useful as a sanity check: a per-binary overhead below this value means the
44
+ * measurement is noise.
45
+ *
46
+ * @param options Sampling policy.
47
+ * @returns Statistics for the cheapest process the platform can start.
48
+ */
49
+ export declare function measureBaselineSpawn(options?: SpawnOverheadOptions): Promise<DurationStatistics>;
50
+ /**
51
+ * Measure spawn overhead for several commands.
52
+ *
53
+ * @param baselines One entry per binary used by the condition.
54
+ * @param options Sampling policy applied to each.
55
+ * @returns Overheads keyed by {@link SpawnBaseline.name}. A command that cannot
56
+ * be executed is omitted rather than failing the calibration.
57
+ */
58
+ export declare function calibrateSpawnOverhead(baselines: readonly SpawnBaseline[], options?: SpawnOverheadOptions): Promise<Map<string, DurationStatistics>>;
59
+ //# sourceMappingURL=spawn-overhead.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spawn-overhead.d.ts","sourceRoot":"","sources":["../src/spawn-overhead.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjD,MAAM,WAAW,oBAAoB;IACpC,uDAAuD;IACvD,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,4CAA4C;IAC5C,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,aAAa;IAC7B,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;IAEb,2EAA2E;IAC3E,OAAO,EAAE,MAAM,CAAC;IAEhB,8DAA8D;IAC9D,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACzB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,oBAAoB,CACzC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,CAAC,EAAE,oBAAoB,GAC5B,OAAO,CAAC,kBAAkB,CAAC,CAiB7B;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CACnC,OAAO,GAAE,oBAAyB,GAChC,OAAO,CAAC,kBAAkB,CAAC,CAI7B;AAED;;;;;;;GAOG;AACH,wBAAsB,sBAAsB,CAC3C,SAAS,EAAE,SAAS,aAAa,EAAE,EACnC,OAAO,CAAC,EAAE,oBAAoB,GAC5B,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAiB1C"}
@@ -0,0 +1,80 @@
1
+ import { isNodeErrorWithCode, spawnProcess } from "@ac-kit/node";
2
+ import { summarise } from "./_statistics.js";
3
+ /**
4
+ * Measure how long it takes to start and stop a command that does nothing.
5
+ *
6
+ * A benchmark that spawns a process per iteration includes this cost in every
7
+ * sample, while an in-process contender pays none of it. Measuring it makes the
8
+ * bias visible and lets reports show an adjusted figure.
9
+ *
10
+ * @param command Executable to run. Resolved on `PATH`; never passed through a
11
+ * shell.
12
+ * @param args Arguments producing the cheapest meaningful invocation.
13
+ * @param options Sampling policy.
14
+ * @returns Statistics over the measured invocations.
15
+ * @throws {Error} If the command cannot be executed at all.
16
+ */
17
+ export async function measureSpawnOverhead(command, args, options) {
18
+ const warmup = options?.warmup ?? 3;
19
+ const runs = options?.runs ?? 20;
20
+ for (let iteration = 0; iteration < warmup; iteration++) {
21
+ await invoke_(command, args);
22
+ }
23
+ const samples = [];
24
+ for (let iteration = 0; iteration < runs; iteration++) {
25
+ const began = performance.now();
26
+ await invoke_(command, args);
27
+ samples.push(performance.now() - began);
28
+ }
29
+ return summarise(samples);
30
+ }
31
+ /**
32
+ * Floor cost of spawning any process from this runtime.
33
+ *
34
+ * Useful as a sanity check: a per-binary overhead below this value means the
35
+ * measurement is noise.
36
+ *
37
+ * @param options Sampling policy.
38
+ * @returns Statistics for the cheapest process the platform can start.
39
+ */
40
+ export function measureBaselineSpawn(options = {}) {
41
+ return process.platform === "win32"
42
+ ? measureSpawnOverhead("cmd", ["/c", "exit"], options)
43
+ : measureSpawnOverhead("true", [], options);
44
+ }
45
+ /**
46
+ * Measure spawn overhead for several commands.
47
+ *
48
+ * @param baselines One entry per binary used by the condition.
49
+ * @param options Sampling policy applied to each.
50
+ * @returns Overheads keyed by {@link SpawnBaseline.name}. A command that cannot
51
+ * be executed is omitted rather than failing the calibration.
52
+ */
53
+ export async function calibrateSpawnOverhead(baselines, options) {
54
+ const overheads = new Map();
55
+ for (const baseline of baselines) {
56
+ try {
57
+ overheads.set(baseline.name, await measureSpawnOverhead(baseline.command, baseline.args ?? [], options));
58
+ }
59
+ catch {
60
+ // An unavailable binary is reported by its own benchmark case.
61
+ }
62
+ }
63
+ return overheads;
64
+ }
65
+ /**
66
+ * Runs `command`, tolerating a nonzero exit — the process still started and
67
+ * stopped, which is what spawn-overhead measurement cares about.
68
+ */
69
+ async function invoke_(command, args) {
70
+ try {
71
+ await spawnProcess(command, args);
72
+ }
73
+ catch (error) {
74
+ if (isNodeErrorWithCode(error, "ENOENT")) {
75
+ throw new Error(`Command not found: ${command}`);
76
+ }
77
+ // A non-zero exit is acceptable; the process still started and stopped.
78
+ }
79
+ }
80
+ //# sourceMappingURL=spawn-overhead.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spawn-overhead.js","sourceRoot":"","sources":["../src/spawn-overhead.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjE,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AA+B7C;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACzC,OAAe,EACf,IAAuB,EACvB,OAA8B;IAE9B,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,IAAI,CAAC,CAAC;IACpC,MAAM,IAAI,GAAG,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC;IAEjC,KAAK,IAAI,SAAS,GAAG,CAAC,EAAE,SAAS,GAAG,MAAM,EAAE,SAAS,EAAE,EAAE,CAAC;QACzD,MAAM,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC9B,CAAC;IAED,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,KAAK,IAAI,SAAS,GAAG,CAAC,EAAE,SAAS,GAAG,IAAI,EAAE,SAAS,EAAE,EAAE,CAAC;QACvD,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;QAChC,MAAM,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAC7B,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC;IACzC,CAAC;IAED,OAAO,SAAS,CAAC,OAAO,CAAC,CAAC;AAC3B,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,oBAAoB,CACnC,OAAO,GAAyB,EAAE;IAElC,OAAO,OAAO,CAAC,QAAQ,KAAK,OAAO;QAClC,CAAC,CAAC,oBAAoB,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC;QACtD,CAAC,CAAC,oBAAoB,CAAC,MAAM,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;AAC9C,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC3C,SAAmC,EACnC,OAA8B;IAE9B,MAAM,SAAS,GAAG,IAAI,GAAG,EAA8B,CAAC;IACxD,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QAClC,IAAI,CAAC;YACJ,SAAS,CAAC,GAAG,CACZ,QAAQ,CAAC,IAAI,EACb,MAAM,oBAAoB,CACzB,QAAQ,CAAC,OAAO,EAChB,QAAQ,CAAC,IAAI,IAAI,EAAE,EACnB,OAAO,CACP,CACD,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACR,+DAA+D;QAChE,CAAC;IACF,CAAC;IACD,OAAO,SAAS,CAAC;AAClB,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,OAAO,CACrB,OAAe,EACf,IAAuB;IAEvB,IAAI,CAAC;QACJ,MAAM,YAAY,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACnC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,IAAI,mBAAmB,CAAC,KAAK,EAAE,QAAQ,CAAC,EAAE,CAAC;YAC1C,MAAM,IAAI,KAAK,CAAC,sBAAsB,OAAO,EAAE,CAAC,CAAC;QAClD,CAAC;QACD,wEAAwE;IACzE,CAAC;AACF,CAAC"}
package/package.json ADDED
@@ -0,0 +1,77 @@
1
+ {
2
+ "name": "@ac-bench/measure-duration",
3
+ "version": "0.1.0",
4
+ "description": "Duration measure for @ac-bench: adaptive sampling, duration statistics and quality warnings for outliers, high variance and overhead dominance.",
5
+ "keywords": [
6
+ "benchmark",
7
+ "duration",
8
+ "sampling",
9
+ "statistics"
10
+ ],
11
+ "homepage": "https://github.com/anthochamp/node-essentials/tree/main/bench/measure-duration#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/measure-duration"
21
+ },
22
+ "files": [
23
+ "dist"
24
+ ],
25
+ "type": "module",
26
+ "sideEffects": false,
27
+ "main": "./dist/index.js",
28
+ "exports": {
29
+ ".": "./dist/index.js",
30
+ "./plugin": "./dist/plugin.js",
31
+ "./iteration-child": "./dist/iteration-child.js"
32
+ },
33
+ "publishConfig": {
34
+ "main": "./dist/index.js",
35
+ "exports": {
36
+ ".": "./dist/index.js",
37
+ "./plugin": "./dist/plugin.js",
38
+ "./iteration-child": "./dist/iteration-child.js"
39
+ },
40
+ "access": "public"
41
+ },
42
+ "scripts": {
43
+ "compile": "tsc -p ./tsconfig.build.json --outDir dist",
44
+ "compile:check": "tsc -noEmit -p ./tsconfig.json",
45
+ "clean": "rimraf dist",
46
+ "build": "yarn clean && yarn compile",
47
+ "test": "yarn run -T vitest run",
48
+ "prepack": "yarn build"
49
+ },
50
+ "dependencies": {
51
+ "@ac-bench/core": "^0.1.0",
52
+ "@ac-bench/util": "^0.1.0",
53
+ "@ac-kit/app-report": "^0.1.0",
54
+ "@ac-kit/app-system": "^0.1.0",
55
+ "@ac-kit/core": "^0.1.0",
56
+ "@ac-kit/math-stats": "^0.1.0",
57
+ "@ac-kit/model-dataset": "^0.1.0",
58
+ "@ac-kit/node": "^0.1.0",
59
+ "@types/node": "^26.5.1",
60
+ "zod": "^4.5.4"
61
+ },
62
+ "devDependencies": {
63
+ "@ac-kit/tsconfig": "0.1.0",
64
+ "@ac-kit/typedoc-config": "0.1.0",
65
+ "@ac-kit/vitest-config": "0.1.0",
66
+ "@typescript/native": "typescript@7.0.2",
67
+ "rimraf": "6.1.3",
68
+ "tsx": "4.23.12",
69
+ "typedoc": "0.28.20",
70
+ "typescript": "@typescript/typescript6@6.0.2",
71
+ "vite": "8.3.0",
72
+ "vitest": "5.0.0"
73
+ },
74
+ "engines": {
75
+ "node": ">=24"
76
+ }
77
+ }