benchforge 0.1.9 → 0.1.11
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 +40 -6
- package/dist/{BenchRunner-CSKN9zPy.d.mts → BenchRunner-BzyUfiyB.d.mts} +32 -8
- package/dist/{BrowserHeapSampler-DCeL42RE.mjs → BrowserHeapSampler-B6asLKWQ.mjs} +57 -57
- package/dist/BrowserHeapSampler-B6asLKWQ.mjs.map +1 -0
- package/dist/{GcStats-ByEovUi1.mjs → GcStats-wX7Xyblu.mjs} +15 -15
- package/dist/GcStats-wX7Xyblu.mjs.map +1 -0
- package/dist/HeapSampler-B8dtKHn1.mjs.map +1 -1
- package/dist/{TimingUtils-ClclVQ7E.mjs → TimingUtils-DwOwkc8G.mjs} +225 -225
- package/dist/TimingUtils-DwOwkc8G.mjs.map +1 -0
- package/dist/bin/benchforge.mjs +1 -1
- package/dist/browser/index.js +210 -210
- package/dist/index.d.mts +102 -46
- package/dist/index.mjs +3 -3
- package/dist/runners/WorkerScript.d.mts +1 -1
- package/dist/runners/WorkerScript.mjs +66 -66
- package/dist/runners/WorkerScript.mjs.map +1 -1
- package/dist/{src-Cf_LXwlp.mjs → src-B-DDaCa9.mjs} +1225 -990
- package/dist/src-B-DDaCa9.mjs.map +1 -0
- package/package.json +2 -1
- package/src/BenchMatrix.ts +125 -125
- package/src/BenchmarkReport.ts +50 -45
- package/src/HtmlDataPrep.ts +21 -21
- package/src/PermutationTest.ts +24 -24
- package/src/StandardSections.ts +45 -45
- package/src/StatisticalUtils.ts +60 -61
- package/src/browser/BrowserGcStats.ts +5 -5
- package/src/browser/BrowserHeapSampler.ts +63 -63
- package/src/cli/CliArgs.ts +6 -3
- package/src/cli/FilterBenchmarks.ts +5 -5
- package/src/cli/RunBenchCLI.ts +526 -498
- package/src/export/JsonExport.ts +10 -10
- package/src/export/PerfettoExport.ts +74 -74
- package/src/export/SpeedscopeExport.ts +202 -0
- package/src/heap-sample/HeapSampleReport.ts +143 -70
- package/src/heap-sample/HeapSampler.ts +55 -12
- package/src/heap-sample/ResolvedProfile.ts +89 -0
- package/src/html/HtmlReport.ts +33 -33
- package/src/html/HtmlTemplate.ts +67 -67
- package/src/html/browser/CIPlot.ts +50 -50
- package/src/html/browser/HistogramKde.ts +13 -13
- package/src/html/browser/LegendUtils.ts +48 -48
- package/src/html/browser/RenderPlots.ts +98 -98
- package/src/html/browser/SampleTimeSeries.ts +79 -79
- package/src/index.ts +6 -0
- package/src/matrix/MatrixFilter.ts +6 -6
- package/src/matrix/MatrixReport.ts +96 -96
- package/src/matrix/VariantLoader.ts +5 -5
- package/src/runners/AdaptiveWrapper.ts +151 -151
- package/src/runners/BasicRunner.ts +175 -175
- package/src/runners/BenchRunner.ts +8 -8
- package/src/runners/GcStats.ts +22 -22
- package/src/runners/RunnerOrchestrator.ts +168 -168
- package/src/runners/WorkerScript.ts +96 -96
- package/src/table-util/Formatters.ts +41 -36
- package/src/table-util/TableReport.ts +122 -122
- package/src/table-util/test/TableValueExtractor.ts +9 -9
- package/src/test/AdaptiveStatistics.integration.ts +7 -39
- package/src/test/HeapAttribution.test.ts +51 -0
- package/src/test/RunBenchCLI.test.ts +18 -18
- package/src/test/TestUtils.ts +24 -24
- package/src/tests/BenchMatrix.test.ts +12 -12
- package/src/tests/MatrixFilter.test.ts +15 -15
- package/dist/BrowserHeapSampler-DCeL42RE.mjs.map +0 -1
- package/dist/GcStats-ByEovUi1.mjs.map +0 -1
- package/dist/TimingUtils-ClclVQ7E.mjs.map +0 -1
- package/dist/src-Cf_LXwlp.mjs.map +0 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as MeasuredResults, i as BenchmarkSpec, n as BenchGroup, r as BenchSuite, t as RunnerOptions } from "./BenchRunner-
|
|
1
|
+
import { a as MeasuredResults, i as BenchmarkSpec, n as BenchGroup, o as HeapProfile, r as BenchSuite, t as RunnerOptions } from "./BenchRunner-BzyUfiyB.mjs";
|
|
2
2
|
import { Alignment } from "table";
|
|
3
3
|
import { Argv, InferredOptionTypes } from "yargs";
|
|
4
4
|
import * as node_http0 from "node:http";
|
|
@@ -59,8 +59,6 @@ interface MatrixResults {
|
|
|
59
59
|
name: string;
|
|
60
60
|
variants: VariantResult[];
|
|
61
61
|
}
|
|
62
|
-
/** @return true if variant is a StatefulVariant (has setup + run) */
|
|
63
|
-
declare function isStatefulVariant<T, S>(v: Variant<T, S>): v is StatefulVariant<T, S>;
|
|
64
62
|
/** Options for runMatrix */
|
|
65
63
|
interface RunMatrixOptions {
|
|
66
64
|
iterations?: number;
|
|
@@ -81,6 +79,8 @@ interface RunMatrixOptions {
|
|
|
81
79
|
heapInterval?: number;
|
|
82
80
|
heapDepth?: number;
|
|
83
81
|
}
|
|
82
|
+
/** @return true if variant is a StatefulVariant (has setup + run) */
|
|
83
|
+
declare function isStatefulVariant<T, S>(v: Variant<T, S>): v is StatefulVariant<T, S>;
|
|
84
84
|
/** Run a BenchMatrix with inline variants or variantDir */
|
|
85
85
|
declare function runMatrix<T>(matrix: BenchMatrix<T>, options?: RunMatrixOptions): Promise<MatrixResults>;
|
|
86
86
|
//#endregion
|
|
@@ -104,7 +104,7 @@ interface ColumnFormat<T> {
|
|
|
104
104
|
alignment?: Alignment;
|
|
105
105
|
width?: number;
|
|
106
106
|
}
|
|
107
|
-
/**
|
|
107
|
+
/** Build formatted table with column groups and baselines */
|
|
108
108
|
//#endregion
|
|
109
109
|
//#region src/BenchmarkReport.d.ts
|
|
110
110
|
/** Benchmark results with optional baseline for comparison */
|
|
@@ -219,11 +219,21 @@ declare const cliOptions: {
|
|
|
219
219
|
readonly requiresArg: true;
|
|
220
220
|
readonly describe: "export benchmark data to JSON file";
|
|
221
221
|
};
|
|
222
|
-
readonly perfetto: {
|
|
222
|
+
readonly "export-perfetto": {
|
|
223
223
|
readonly type: "string";
|
|
224
224
|
readonly requiresArg: true;
|
|
225
225
|
readonly describe: "export Perfetto trace file (view at ui.perfetto.dev)";
|
|
226
226
|
};
|
|
227
|
+
readonly speedscope: {
|
|
228
|
+
readonly type: "boolean";
|
|
229
|
+
readonly default: false;
|
|
230
|
+
readonly describe: "open heap profile in speedscope (via npx)";
|
|
231
|
+
};
|
|
232
|
+
readonly "export-speedscope": {
|
|
233
|
+
readonly type: "string";
|
|
234
|
+
readonly requiresArg: true;
|
|
235
|
+
readonly describe: "export heap profile as speedscope JSON";
|
|
236
|
+
};
|
|
227
237
|
readonly "trace-opt": {
|
|
228
238
|
readonly type: "boolean";
|
|
229
239
|
readonly default: false;
|
|
@@ -288,6 +298,11 @@ declare const cliOptions: {
|
|
|
288
298
|
readonly default: false;
|
|
289
299
|
readonly describe: "verbose output with file:// paths and line numbers";
|
|
290
300
|
};
|
|
301
|
+
readonly "heap-raw": {
|
|
302
|
+
readonly type: "boolean";
|
|
303
|
+
readonly default: false;
|
|
304
|
+
readonly describe: "dump every raw heap sample (ordinal, size, stack)";
|
|
305
|
+
};
|
|
291
306
|
readonly "heap-user-only": {
|
|
292
307
|
readonly type: "boolean";
|
|
293
308
|
readonly default: false;
|
|
@@ -424,13 +439,14 @@ interface CallFrame {
|
|
|
424
439
|
fn: string;
|
|
425
440
|
url: string;
|
|
426
441
|
line: number;
|
|
427
|
-
col
|
|
442
|
+
col?: number;
|
|
428
443
|
}
|
|
429
444
|
type UserCodeFilter = (site: CallFrame) => boolean;
|
|
430
445
|
interface HeapReportOptions {
|
|
431
446
|
topN: number;
|
|
432
447
|
stackDepth?: number;
|
|
433
448
|
verbose?: boolean;
|
|
449
|
+
raw?: boolean;
|
|
434
450
|
userOnly?: boolean;
|
|
435
451
|
isUserCode?: UserCodeFilter;
|
|
436
452
|
totalAll?: number;
|
|
@@ -453,16 +469,29 @@ interface MatrixReportOptions {
|
|
|
453
469
|
sections?: ResultsMapper[];
|
|
454
470
|
variantTitle?: string;
|
|
455
471
|
}
|
|
456
|
-
/** Format matrix results as one table per case */
|
|
457
|
-
declare function reportMatrixResults(results: MatrixResults, options?: MatrixReportOptions): string;
|
|
458
472
|
/** GC statistics columns - derived from gcStatsSection for consistency */
|
|
459
473
|
declare const gcStatsColumns: ExtraColumn[];
|
|
460
474
|
/** GC pause time column */
|
|
461
475
|
declare const gcPauseColumn: ExtraColumn;
|
|
462
476
|
/** Heap sampling total bytes column */
|
|
463
477
|
declare const heapTotalColumn: ExtraColumn;
|
|
478
|
+
/** Format matrix results as one table per case */
|
|
479
|
+
declare function reportMatrixResults(results: MatrixResults, options?: MatrixReportOptions): string;
|
|
464
480
|
//#endregion
|
|
465
481
|
//#region src/cli/RunBenchCLI.d.ts
|
|
482
|
+
interface ExportOptions {
|
|
483
|
+
results: ReportGroup[];
|
|
484
|
+
args: DefaultCliArgs;
|
|
485
|
+
sections?: any[];
|
|
486
|
+
suiteName?: string;
|
|
487
|
+
currentVersion?: GitVersion;
|
|
488
|
+
baselineVersion?: GitVersion;
|
|
489
|
+
}
|
|
490
|
+
interface MatrixExportOptions {
|
|
491
|
+
sections?: any[];
|
|
492
|
+
currentVersion?: GitVersion;
|
|
493
|
+
baselineVersion?: GitVersion;
|
|
494
|
+
}
|
|
466
495
|
/** Parse CLI with custom configuration */
|
|
467
496
|
declare function parseBenchArgs<T = DefaultCliArgs>(configureArgs?: Configure<T>): T & DefaultCliArgs;
|
|
468
497
|
/** Run suite with CLI arguments */
|
|
@@ -479,14 +508,6 @@ declare function runDefaultBench(suite?: BenchSuite, configureArgs?: Configure<a
|
|
|
479
508
|
declare function reportOptStatus(groups: ReportGroup[]): void;
|
|
480
509
|
/** @return true if any result has the specified field with a defined value */
|
|
481
510
|
declare function hasField(results: ReportGroup[], field: keyof MeasuredResults): boolean;
|
|
482
|
-
interface ExportOptions {
|
|
483
|
-
results: ReportGroup[];
|
|
484
|
-
args: DefaultCliArgs;
|
|
485
|
-
sections?: any[];
|
|
486
|
-
suiteName?: string;
|
|
487
|
-
currentVersion?: GitVersion;
|
|
488
|
-
baselineVersion?: GitVersion;
|
|
489
|
-
}
|
|
490
511
|
/** Export reports (HTML, JSON, Perfetto) based on CLI args */
|
|
491
512
|
declare function exportReports(options: ExportOptions): Promise<void>;
|
|
492
513
|
/** Run matrix suite with CLI arguments.
|
|
@@ -501,11 +522,6 @@ declare function defaultMatrixReport(results: MatrixResults[], reportOptions?: M
|
|
|
501
522
|
declare function runDefaultMatrixBench(suite: MatrixSuite, configureArgs?: Configure<any>, reportOptions?: MatrixReportOptions): Promise<void>;
|
|
502
523
|
/** Convert MatrixResults to ReportGroup[] for export compatibility */
|
|
503
524
|
declare function matrixToReportGroups(results: MatrixResults[]): ReportGroup[];
|
|
504
|
-
interface MatrixExportOptions {
|
|
505
|
-
sections?: any[];
|
|
506
|
-
currentVersion?: GitVersion;
|
|
507
|
-
baselineVersion?: GitVersion;
|
|
508
|
-
}
|
|
509
525
|
/** Run matrix benchmarks, display table, and generate exports */
|
|
510
526
|
declare function matrixBenchExports(suite: MatrixSuite, args: DefaultCliArgs, reportOptions?: MatrixReportOptions, exportOptions?: MatrixExportOptions): Promise<void>;
|
|
511
527
|
//#endregion
|
|
@@ -595,6 +611,43 @@ interface BenchmarkResult {
|
|
|
595
611
|
/** Export benchmark results to Perfetto-compatible trace file */
|
|
596
612
|
declare function exportPerfettoTrace(groups: ReportGroup[], outputPath: string, args: DefaultCliArgs): void;
|
|
597
613
|
//#endregion
|
|
614
|
+
//#region src/export/SpeedscopeExport.d.ts
|
|
615
|
+
/** speedscope file format (https://www.speedscope.app/file-format-schema.json) */
|
|
616
|
+
interface SpeedscopeFile {
|
|
617
|
+
$schema: "https://www.speedscope.app/file-format-schema.json";
|
|
618
|
+
shared: {
|
|
619
|
+
frames: SpeedscopeFrame[];
|
|
620
|
+
};
|
|
621
|
+
profiles: SpeedscopeProfile[];
|
|
622
|
+
name?: string;
|
|
623
|
+
exporter?: string;
|
|
624
|
+
}
|
|
625
|
+
interface SpeedscopeFrame {
|
|
626
|
+
name: string;
|
|
627
|
+
file?: string;
|
|
628
|
+
line?: number;
|
|
629
|
+
col?: number;
|
|
630
|
+
}
|
|
631
|
+
interface SpeedscopeProfile {
|
|
632
|
+
type: "sampled";
|
|
633
|
+
name: string;
|
|
634
|
+
unit: "bytes";
|
|
635
|
+
startValue: number;
|
|
636
|
+
endValue: number;
|
|
637
|
+
samples: number[][];
|
|
638
|
+
weights: number[];
|
|
639
|
+
}
|
|
640
|
+
/** Export heap profiles from benchmark results to speedscope JSON format.
|
|
641
|
+
* Creates one speedscope profile per benchmark that has a heapProfile.
|
|
642
|
+
* @returns resolved output path, or undefined if no profiles were found */
|
|
643
|
+
declare function exportSpeedscope(groups: ReportGroup[], outputPath: string): string | undefined;
|
|
644
|
+
/** Export to a temp file and open in speedscope via npx */
|
|
645
|
+
declare function exportAndLaunchSpeedscope(groups: ReportGroup[]): void;
|
|
646
|
+
/** Launch speedscope viewer on a file via npx */
|
|
647
|
+
declare function launchSpeedscope(filePath: string): void;
|
|
648
|
+
/** Convert a single HeapProfile to speedscope format (for standalone use) */
|
|
649
|
+
declare function heapProfileToSpeedscope(name: string, profile: HeapProfile): SpeedscopeFile;
|
|
650
|
+
//#endregion
|
|
598
651
|
//#region src/HtmlDataPrep.d.ts
|
|
599
652
|
interface PrepareHtmlOptions {
|
|
600
653
|
cliArgs?: Record<string, unknown>;
|
|
@@ -624,13 +677,13 @@ interface MatrixFilter {
|
|
|
624
677
|
case?: string;
|
|
625
678
|
variant?: string;
|
|
626
679
|
}
|
|
627
|
-
/** Parse filter string: "case/variant", "case/", "/variant", or "case" */
|
|
628
|
-
declare function parseMatrixFilter(filter: string): MatrixFilter;
|
|
629
680
|
/** Filtered matrix with explicit case and variant lists */
|
|
630
681
|
interface FilteredMatrix<T = unknown> extends BenchMatrix<T> {
|
|
631
682
|
filteredCases?: string[];
|
|
632
683
|
filteredVariants?: string[];
|
|
633
684
|
}
|
|
685
|
+
/** Parse filter string: "case/variant", "case/", "/variant", or "case" */
|
|
686
|
+
declare function parseMatrixFilter(filter: string): MatrixFilter;
|
|
634
687
|
/** Apply filter to a matrix, merging with existing filters via intersection */
|
|
635
688
|
declare function filterMatrix<T>(matrix: FilteredMatrix<T>, filter?: MatrixFilter): Promise<FilteredMatrix<T>>;
|
|
636
689
|
//#endregion
|
|
@@ -640,13 +693,9 @@ interface TimeStats {
|
|
|
640
693
|
p50?: number;
|
|
641
694
|
p99?: number;
|
|
642
695
|
}
|
|
643
|
-
/** Section: mean, p50, p99 timing */
|
|
644
|
-
declare const timeSection: ResultsMapper<TimeStats>;
|
|
645
696
|
interface GcSectionStats {
|
|
646
697
|
gc?: number;
|
|
647
698
|
}
|
|
648
|
-
/** Section: GC time as fraction of total benchmark time (Node performance hooks) */
|
|
649
|
-
declare const gcSection: ResultsMapper<GcSectionStats>;
|
|
650
699
|
interface GcStatsInfo {
|
|
651
700
|
allocPerIter?: number;
|
|
652
701
|
collected?: number;
|
|
@@ -655,42 +704,46 @@ interface GcStatsInfo {
|
|
|
655
704
|
promoPercent?: number;
|
|
656
705
|
pausePerIter?: number;
|
|
657
706
|
}
|
|
658
|
-
/** Section: detailed GC stats from --trace-gc-nvp (allocation, promotion, pauses) */
|
|
659
|
-
declare const gcStatsSection: ResultsMapper<GcStatsInfo>;
|
|
660
707
|
interface CpuStats {
|
|
661
708
|
cpuCacheMiss?: number;
|
|
662
709
|
cpuStall?: number;
|
|
663
710
|
}
|
|
664
|
-
/** Section: CPU L1 cache miss rate and stall rate (requires @mitata/counters) */
|
|
665
|
-
declare const cpuSection: ResultsMapper<CpuStats>;
|
|
666
711
|
interface RunStats {
|
|
667
712
|
runs?: number;
|
|
668
713
|
}
|
|
669
|
-
/** Section: number of sample iterations */
|
|
670
|
-
declare const runsSection: ResultsMapper<RunStats>;
|
|
671
|
-
/** Section: total sampling duration in seconds (brackets if >= 30s) */
|
|
672
|
-
declare const totalTimeSection: ResultsMapper<{
|
|
673
|
-
totalTime?: number;
|
|
674
|
-
}>;
|
|
675
714
|
interface AdaptiveStats {
|
|
676
715
|
median?: number;
|
|
677
716
|
mean?: number;
|
|
678
717
|
p99?: number;
|
|
679
718
|
convergence?: number;
|
|
680
719
|
}
|
|
720
|
+
interface OptStats {
|
|
721
|
+
tiers?: string;
|
|
722
|
+
deopt?: number;
|
|
723
|
+
}
|
|
724
|
+
/** Section: mean, p50, p99 timing */
|
|
725
|
+
declare const timeSection: ResultsMapper<TimeStats>;
|
|
726
|
+
/** Section: GC time as fraction of total benchmark time (Node performance hooks) */
|
|
727
|
+
declare const gcSection: ResultsMapper<GcSectionStats>;
|
|
728
|
+
/** Section: detailed GC stats from --trace-gc-nvp (allocation, promotion, pauses) */
|
|
729
|
+
declare const gcStatsSection: ResultsMapper<GcStatsInfo>;
|
|
730
|
+
/** Section: CPU L1 cache miss rate and stall rate (requires @mitata/counters) */
|
|
731
|
+
declare const cpuSection: ResultsMapper<CpuStats>;
|
|
732
|
+
/** Section: number of sample iterations */
|
|
733
|
+
declare const runsSection: ResultsMapper<RunStats>;
|
|
734
|
+
/** Section: total sampling duration in seconds (brackets if >= 30s) */
|
|
735
|
+
declare const totalTimeSection: ResultsMapper<{
|
|
736
|
+
totalTime?: number;
|
|
737
|
+
}>;
|
|
681
738
|
/** Section: median, mean, p99, and convergence for adaptive mode */
|
|
682
739
|
declare const adaptiveSection: ResultsMapper<AdaptiveStats>;
|
|
740
|
+
/** Section: V8 optimization tier distribution and deopt count */
|
|
741
|
+
declare const optSection: ResultsMapper<OptStats>;
|
|
683
742
|
/** Build generic sections based on CLI flags */
|
|
684
743
|
declare function buildGenericSections(args: {
|
|
685
744
|
"gc-stats"?: boolean;
|
|
686
745
|
"heap-sample"?: boolean;
|
|
687
746
|
}): ResultsMapper[];
|
|
688
|
-
interface OptStats {
|
|
689
|
-
tiers?: string;
|
|
690
|
-
deopt?: number;
|
|
691
|
-
}
|
|
692
|
-
/** Section: V8 optimization tier distribution and deopt count */
|
|
693
|
-
declare const optSection: ResultsMapper<OptStats>;
|
|
694
747
|
//#endregion
|
|
695
748
|
//#region src/StatisticalUtils.d.ts
|
|
696
749
|
/** @return mean of values */
|
|
@@ -705,10 +758,13 @@ declare function formatConvergence(v: unknown): string;
|
|
|
705
758
|
declare function timeMs(ms: unknown): string | null;
|
|
706
759
|
/** Format integer with thousand separators */
|
|
707
760
|
declare function integer(x: unknown): string | null;
|
|
708
|
-
/** Format bytes with appropriate units (B, KB, MB, GB)
|
|
709
|
-
|
|
761
|
+
/** Format bytes with appropriate units (B, KB, MB, GB).
|
|
762
|
+
* Use `space: true` for human-readable console output (`1.5 KB`). */
|
|
763
|
+
declare function formatBytes(bytes: unknown, opts?: {
|
|
764
|
+
space?: boolean;
|
|
765
|
+
}): string | null;
|
|
710
766
|
/** @return truncated string with ellipsis if over maxLen */
|
|
711
767
|
declare function truncate(str: string, maxLen?: number): string;
|
|
712
768
|
//#endregion
|
|
713
|
-
export { type AnyVariant, type BenchGroup, type BenchMatrix, type BenchSuite, BenchmarkGroup, BenchmarkJsonData, type BenchmarkReport, BenchmarkResult, type BenchmarkSpec, BenchmarkSuite, type CaseResult, type CasesModule, type Configure, type DefaultCliArgs, type ExportOptions, type ExtraColumn, type FilteredMatrix, type GitVersion, type HtmlReportOptions, type LoadedCase, type MatrixDefaults, type MatrixExportOptions, type MatrixFilter, type MatrixReportOptions, type MatrixResults, type MatrixSuite, type MeasuredResults, type PrepareHtmlOptions, type ReportColumnGroup, type ReportData, type ReportGroup, type ResultsMapper, type RunMatrixOptions, type RunnerOptions, type StatefulVariant, type UnknownRecord, type Variant, type VariantFn, type VariantResult, adaptiveSection, average, benchExports, buildGenericSections, cliToMatrixOptions, cpuSection, defaultCliArgs, defaultMatrixReport, defaultReport, exportPerfettoTrace, exportReports, filterMatrix, formatBytes, formatConvergence, formatDateWithTimezone, formatGitVersion, gcPauseColumn, gcSection, gcStatsColumns, gcStatsSection, generateHtmlReport, getBaselineVersion, getCurrentGitVersion, hasField, heapTotalColumn, integer, isStatefulVariant, loadCaseData, loadCasesModule, matrixBenchExports, matrixToReportGroups, optSection, parseBenchArgs, parseCliArgs, parseMatrixFilter, prepareHtmlData, printHeapReports, reportMatrixResults, reportOptStatus, reportResults, runBenchmarks, runDefaultBench, runDefaultMatrixBench, runMatrix, runMatrixSuite, runsSection, timeMs, timeSection, totalTimeSection, truncate };
|
|
769
|
+
export { type AnyVariant, type BenchGroup, type BenchMatrix, type BenchSuite, BenchmarkGroup, BenchmarkJsonData, type BenchmarkReport, BenchmarkResult, type BenchmarkSpec, BenchmarkSuite, type CaseResult, type CasesModule, type Configure, type DefaultCliArgs, type ExportOptions, type ExtraColumn, type FilteredMatrix, type GitVersion, type HtmlReportOptions, type LoadedCase, type MatrixDefaults, type MatrixExportOptions, type MatrixFilter, type MatrixReportOptions, type MatrixResults, type MatrixSuite, type MeasuredResults, type PrepareHtmlOptions, type ReportColumnGroup, type ReportData, type ReportGroup, type ResultsMapper, type RunMatrixOptions, type RunnerOptions, type StatefulVariant, type UnknownRecord, type Variant, type VariantFn, type VariantResult, adaptiveSection, average, benchExports, buildGenericSections, cliToMatrixOptions, cpuSection, defaultCliArgs, defaultMatrixReport, defaultReport, exportAndLaunchSpeedscope, exportPerfettoTrace, exportReports, exportSpeedscope, filterMatrix, formatBytes, formatConvergence, formatDateWithTimezone, formatGitVersion, gcPauseColumn, gcSection, gcStatsColumns, gcStatsSection, generateHtmlReport, getBaselineVersion, getCurrentGitVersion, hasField, heapProfileToSpeedscope, heapTotalColumn, integer, isStatefulVariant, launchSpeedscope, loadCaseData, loadCasesModule, matrixBenchExports, matrixToReportGroups, optSection, parseBenchArgs, parseCliArgs, parseMatrixFilter, prepareHtmlData, printHeapReports, reportMatrixResults, reportOptStatus, reportResults, runBenchmarks, runDefaultBench, runDefaultMatrixBench, runMatrix, runMatrixSuite, runsSection, timeMs, timeSection, totalTimeSection, truncate };
|
|
714
770
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as timeSection, B as
|
|
2
|
-
import { o as average } from "./TimingUtils-
|
|
1
|
+
import { $ as loadCasesModule, A as timeSection, B as heapProfileToSpeedscope, C as adaptiveSection, D as gcStatsSection, E as gcSection, F as generateHtmlReport, G as reportResults, H as exportPerfettoTrace, I as formatDateWithTimezone, J as timeMs, K as formatBytes, L as prepareHtmlData, M as formatConvergence, N as filterMatrix, O as optSection, P as parseMatrixFilter, Q as loadCaseData, R as exportAndLaunchSpeedscope, S as reportMatrixResults, T as cpuSection, U as defaultCliArgs, V as launchSpeedscope, W as parseCliArgs, X as isStatefulVariant, Y as truncate, Z as runMatrix, _ as runDefaultMatrixBench, a as cliToMatrixOptions, b as gcStatsColumns, c as exportReports, d as matrixToReportGroups, f as parseBenchArgs, g as runDefaultBench, h as runBenchmarks, i as benchExports, j as totalTimeSection, k as runsSection, l as hasField, m as reportOptStatus, n as getBaselineVersion, o as defaultMatrixReport, p as printHeapReports, q as integer, r as getCurrentGitVersion, s as defaultReport, t as formatGitVersion, u as matrixBenchExports, v as runMatrixSuite, w as buildGenericSections, x as heapTotalColumn, y as gcPauseColumn, z as exportSpeedscope } from "./src-B-DDaCa9.mjs";
|
|
2
|
+
import { o as average } from "./TimingUtils-DwOwkc8G.mjs";
|
|
3
3
|
|
|
4
|
-
export { adaptiveSection, average, benchExports, buildGenericSections, cliToMatrixOptions, cpuSection, defaultCliArgs, defaultMatrixReport, defaultReport, exportPerfettoTrace, exportReports, filterMatrix, formatBytes, formatConvergence, formatDateWithTimezone, formatGitVersion, gcPauseColumn, gcSection, gcStatsColumns, gcStatsSection, generateHtmlReport, getBaselineVersion, getCurrentGitVersion, hasField, heapTotalColumn, integer, isStatefulVariant, loadCaseData, loadCasesModule, matrixBenchExports, matrixToReportGroups, optSection, parseBenchArgs, parseCliArgs, parseMatrixFilter, prepareHtmlData, printHeapReports, reportMatrixResults, reportOptStatus, reportResults, runBenchmarks, runDefaultBench, runDefaultMatrixBench, runMatrix, runMatrixSuite, runsSection, timeMs, timeSection, totalTimeSection, truncate };
|
|
4
|
+
export { adaptiveSection, average, benchExports, buildGenericSections, cliToMatrixOptions, cpuSection, defaultCliArgs, defaultMatrixReport, defaultReport, exportAndLaunchSpeedscope, exportPerfettoTrace, exportReports, exportSpeedscope, filterMatrix, formatBytes, formatConvergence, formatDateWithTimezone, formatGitVersion, gcPauseColumn, gcSection, gcStatsColumns, gcStatsSection, generateHtmlReport, getBaselineVersion, getCurrentGitVersion, hasField, heapProfileToSpeedscope, heapTotalColumn, integer, isStatefulVariant, launchSpeedscope, loadCaseData, loadCasesModule, matrixBenchExports, matrixToReportGroups, optSection, parseBenchArgs, parseCliArgs, parseMatrixFilter, prepareHtmlData, printHeapReports, reportMatrixResults, reportOptStatus, reportResults, runBenchmarks, runDefaultBench, runDefaultMatrixBench, runMatrix, runMatrixSuite, runsSection, timeMs, timeSection, totalTimeSection, truncate };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as MeasuredResults, i as BenchmarkSpec, o as HeapProfile, t as RunnerOptions } from "../BenchRunner-
|
|
1
|
+
import { a as MeasuredResults, i as BenchmarkSpec, o as HeapProfile, t as RunnerOptions } from "../BenchRunner-BzyUfiyB.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/runners/CreateRunner.d.ts
|
|
4
4
|
type KnownRunner = "basic";
|
|
@@ -1,61 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as createAdaptiveWrapper, d as variantModuleUrl, i as createRunner, n as getElapsed, r as getPerfNow, t as debugWorkerTiming } from "../TimingUtils-
|
|
2
|
+
import { a as createAdaptiveWrapper, d as variantModuleUrl, i as createRunner, n as getElapsed, r as getPerfNow, t as debugWorkerTiming } from "../TimingUtils-DwOwkc8G.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/runners/WorkerScript.ts
|
|
5
5
|
const workerStartTime = getPerfNow();
|
|
6
6
|
const maxLifetime = 300 * 1e3;
|
|
7
|
-
/**
|
|
8
|
-
* Worker process for isolated benchmark execution.
|
|
9
|
-
* Uses eval() safely in isolated child process with trusted code.
|
|
10
|
-
*/
|
|
11
|
-
process.on("message", async (message) => {
|
|
12
|
-
if (message.type !== "run") return;
|
|
13
|
-
logTiming(`Processing ${message.spec.name} with ${message.runnerName}`);
|
|
14
|
-
try {
|
|
15
|
-
const start = getPerfNow();
|
|
16
|
-
const baseRunner = await createRunner(message.runnerName);
|
|
17
|
-
const runner = message.options.adaptive ? createAdaptiveWrapper(baseRunner, message.options) : baseRunner;
|
|
18
|
-
logTiming("Runner created in", getElapsed(start));
|
|
19
|
-
const benchStart = getPerfNow();
|
|
20
|
-
if (message.options.heapSample) {
|
|
21
|
-
const { withHeapSampling } = await import("../HeapSampler-B8dtKHn1.mjs");
|
|
22
|
-
const { result: results, profile: heapProfile } = await withHeapSampling({
|
|
23
|
-
samplingInterval: message.options.heapInterval,
|
|
24
|
-
stackDepth: message.options.heapDepth
|
|
25
|
-
}, async () => {
|
|
26
|
-
const { fn, params } = await resolveBenchmarkFn(message);
|
|
27
|
-
return runner.runBench({
|
|
28
|
-
...message.spec,
|
|
29
|
-
fn
|
|
30
|
-
}, message.options, params);
|
|
31
|
-
});
|
|
32
|
-
logTiming("Benchmark execution took", getElapsed(benchStart));
|
|
33
|
-
sendAndExit({
|
|
34
|
-
type: "result",
|
|
35
|
-
results,
|
|
36
|
-
heapProfile
|
|
37
|
-
}, 0);
|
|
38
|
-
} else {
|
|
39
|
-
const { fn, params } = await resolveBenchmarkFn(message);
|
|
40
|
-
const results = await runner.runBench({
|
|
41
|
-
...message.spec,
|
|
42
|
-
fn
|
|
43
|
-
}, message.options, params);
|
|
44
|
-
logTiming("Benchmark execution took", getElapsed(benchStart));
|
|
45
|
-
sendAndExit({
|
|
46
|
-
type: "result",
|
|
47
|
-
results
|
|
48
|
-
}, 0);
|
|
49
|
-
}
|
|
50
|
-
} catch (error) {
|
|
51
|
-
sendAndExit(createErrorMessage(error), 1);
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
setTimeout(() => {
|
|
55
|
-
console.error("WorkerScript: Maximum lifetime exceeded, exiting");
|
|
56
|
-
process.exit(1);
|
|
57
|
-
}, maxLifetime);
|
|
58
|
-
process.stdin.pause();
|
|
59
7
|
/** Log timing with consistent format */
|
|
60
8
|
const logTiming = debugWorkerTiming ? _logTiming : () => {};
|
|
61
9
|
function _logTiming(operation, duration) {
|
|
@@ -104,13 +52,6 @@ async function importVariantModule(message) {
|
|
|
104
52
|
params: void 0
|
|
105
53
|
};
|
|
106
54
|
}
|
|
107
|
-
/** Load case data from a cases module */
|
|
108
|
-
async function loadCaseFromModule(casesModuleUrl, caseId) {
|
|
109
|
-
logTiming(`Loading case '${caseId}' from ${casesModuleUrl}`);
|
|
110
|
-
const module = await import(casesModuleUrl);
|
|
111
|
-
if (typeof module.loadCase === "function") return module.loadCase(caseId);
|
|
112
|
-
return { data: caseId };
|
|
113
|
-
}
|
|
114
55
|
/** Import benchmark function and optionally run setup */
|
|
115
56
|
async function importBenchmarkWithSetup(message) {
|
|
116
57
|
const { modulePath, exportName, setupExportName, params } = message;
|
|
@@ -129,6 +70,19 @@ async function importBenchmarkWithSetup(message) {
|
|
|
129
70
|
params
|
|
130
71
|
};
|
|
131
72
|
}
|
|
73
|
+
/** Reconstruct function from string code */
|
|
74
|
+
function reconstructFunction(fnCode) {
|
|
75
|
+
const fn = eval(`(${fnCode})`);
|
|
76
|
+
if (typeof fn !== "function") throw new Error("Reconstructed code is not a function");
|
|
77
|
+
return fn;
|
|
78
|
+
}
|
|
79
|
+
/** Load case data from a cases module */
|
|
80
|
+
async function loadCaseFromModule(casesModuleUrl, caseId) {
|
|
81
|
+
logTiming(`Loading case '${caseId}' from ${casesModuleUrl}`);
|
|
82
|
+
const module = await import(casesModuleUrl);
|
|
83
|
+
if (typeof module.loadCase === "function") return module.loadCase(caseId);
|
|
84
|
+
return { data: caseId };
|
|
85
|
+
}
|
|
132
86
|
/** Get named or default export from module */
|
|
133
87
|
function getModuleExport(module, exportName, modulePath) {
|
|
134
88
|
const fn = exportName ? module[exportName] : module.default || module;
|
|
@@ -138,12 +92,6 @@ function getModuleExport(module, exportName, modulePath) {
|
|
|
138
92
|
}
|
|
139
93
|
return fn;
|
|
140
94
|
}
|
|
141
|
-
/** Reconstruct function from string code */
|
|
142
|
-
function reconstructFunction(fnCode) {
|
|
143
|
-
const fn = eval(`(${fnCode})`);
|
|
144
|
-
if (typeof fn !== "function") throw new Error("Reconstructed code is not a function");
|
|
145
|
-
return fn;
|
|
146
|
-
}
|
|
147
95
|
/** Create error message from exception */
|
|
148
96
|
function createErrorMessage(error) {
|
|
149
97
|
return {
|
|
@@ -152,6 +100,58 @@ function createErrorMessage(error) {
|
|
|
152
100
|
stack: error instanceof Error ? error.stack : void 0
|
|
153
101
|
};
|
|
154
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* Worker process for isolated benchmark execution.
|
|
105
|
+
* Uses eval() safely in isolated child process with trusted code.
|
|
106
|
+
*/
|
|
107
|
+
process.on("message", async (message) => {
|
|
108
|
+
if (message.type !== "run") return;
|
|
109
|
+
logTiming(`Processing ${message.spec.name} with ${message.runnerName}`);
|
|
110
|
+
try {
|
|
111
|
+
const start = getPerfNow();
|
|
112
|
+
const baseRunner = await createRunner(message.runnerName);
|
|
113
|
+
const runner = message.options.adaptive ? createAdaptiveWrapper(baseRunner, message.options) : baseRunner;
|
|
114
|
+
logTiming("Runner created in", getElapsed(start));
|
|
115
|
+
const benchStart = getPerfNow();
|
|
116
|
+
if (message.options.heapSample) {
|
|
117
|
+
const { withHeapSampling } = await import("../HeapSampler-B8dtKHn1.mjs");
|
|
118
|
+
const { result: results, profile: heapProfile } = await withHeapSampling({
|
|
119
|
+
samplingInterval: message.options.heapInterval,
|
|
120
|
+
stackDepth: message.options.heapDepth
|
|
121
|
+
}, async () => {
|
|
122
|
+
const { fn, params } = await resolveBenchmarkFn(message);
|
|
123
|
+
return runner.runBench({
|
|
124
|
+
...message.spec,
|
|
125
|
+
fn
|
|
126
|
+
}, message.options, params);
|
|
127
|
+
});
|
|
128
|
+
logTiming("Benchmark execution took", getElapsed(benchStart));
|
|
129
|
+
sendAndExit({
|
|
130
|
+
type: "result",
|
|
131
|
+
results,
|
|
132
|
+
heapProfile
|
|
133
|
+
}, 0);
|
|
134
|
+
} else {
|
|
135
|
+
const { fn, params } = await resolveBenchmarkFn(message);
|
|
136
|
+
const results = await runner.runBench({
|
|
137
|
+
...message.spec,
|
|
138
|
+
fn
|
|
139
|
+
}, message.options, params);
|
|
140
|
+
logTiming("Benchmark execution took", getElapsed(benchStart));
|
|
141
|
+
sendAndExit({
|
|
142
|
+
type: "result",
|
|
143
|
+
results
|
|
144
|
+
}, 0);
|
|
145
|
+
}
|
|
146
|
+
} catch (error) {
|
|
147
|
+
sendAndExit(createErrorMessage(error), 1);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
setTimeout(() => {
|
|
151
|
+
console.error("WorkerScript: Maximum lifetime exceeded, exiting");
|
|
152
|
+
process.exit(1);
|
|
153
|
+
}, maxLifetime);
|
|
154
|
+
process.stdin.pause();
|
|
155
155
|
|
|
156
156
|
//#endregion
|
|
157
157
|
export { };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkerScript.mjs","names":[],"sources":["../../src/runners/WorkerScript.ts"],"sourcesContent":["#!/usr/bin/env node\nimport type { BenchmarkFunction, BenchmarkSpec } from \"../Benchmark.ts\";\nimport type { HeapProfile } from \"../heap-sample/HeapSampler.ts\";\nimport type { MeasuredResults } from \"../MeasuredResults.ts\";\nimport { variantModuleUrl } from \"../matrix/VariantLoader.ts\";\nimport {\n type AdaptiveOptions,\n createAdaptiveWrapper,\n} from \"./AdaptiveWrapper.ts\";\nimport type { RunnerOptions } from \"./BenchRunner.ts\";\nimport { createRunner, type KnownRunner } from \"./CreateRunner.ts\";\nimport { debugWorkerTiming, getElapsed, getPerfNow } from \"./TimingUtils.ts\";\n\nconst workerStartTime = getPerfNow();\nconst maxLifetime = 5 * 60 * 1000; // 5 minutes\n\n/** Message sent to worker process to start a benchmark run. */\nexport interface RunMessage {\n type: \"run\";\n spec: BenchmarkSpec;\n runnerName: KnownRunner;\n options: RunnerOptions;\n fnCode?: string; // Made optional - either fnCode or modulePath is required\n modulePath?: string; // Path to module for dynamic import\n exportName?: string; // Export name from module\n setupExportName?: string; // Setup function export name - called once, result passed to fn\n params?: unknown;\n // Variant directory mode (BenchMatrix)\n variantDir?: string; // Directory URL containing variant .ts files\n variantId?: string; // Variant filename (without .ts)\n caseData?: unknown; // Data to pass to variant\n caseId?: string; // Case identifier\n casesModule?: string; // URL to cases module (exports cases[] and loadCase())\n}\n\n/** Message returned from worker process with benchmark results. */\nexport interface ResultMessage {\n type: \"result\";\n results: MeasuredResults[];\n heapProfile?: HeapProfile;\n}\n\n/** Message returned from worker process when benchmark fails. */\nexport interface ErrorMessage {\n type: \"error\";\n error: string;\n stack?: string;\n}\n\nexport type WorkerMessage = RunMessage | ResultMessage | ErrorMessage;\n\n/**\n * Worker process for isolated benchmark execution.\n * Uses eval() safely in isolated child process with trusted code.\n */\nprocess.on(\"message\", async (message: RunMessage) => {\n if (message.type !== \"run\") return;\n\n logTiming(`Processing ${message.spec.name} with ${message.runnerName}`);\n\n try {\n const start = getPerfNow();\n const baseRunner = await createRunner(message.runnerName);\n\n const runner = (message.options as any).adaptive\n ? createAdaptiveWrapper(baseRunner, message.options as AdaptiveOptions)\n : baseRunner;\n\n logTiming(\"Runner created in\", getElapsed(start));\n\n const benchStart = getPerfNow();\n\n // Run with heap sampling if enabled (covers module import + execution)\n if (message.options.heapSample) {\n const { withHeapSampling } = await import(\n \"../heap-sample/HeapSampler.ts\"\n );\n const heapOpts = {\n samplingInterval: message.options.heapInterval,\n stackDepth: message.options.heapDepth,\n };\n const { result: results, profile: heapProfile } = await withHeapSampling(\n heapOpts,\n async () => {\n const { fn, params } = await resolveBenchmarkFn(message);\n return runner.runBench(\n { ...message.spec, fn },\n message.options,\n params,\n );\n },\n );\n logTiming(\"Benchmark execution took\", getElapsed(benchStart));\n sendAndExit({ type: \"result\", results, heapProfile }, 0);\n } else {\n const { fn, params } = await resolveBenchmarkFn(message);\n const results = await runner.runBench(\n { ...message.spec, fn },\n message.options,\n params,\n );\n logTiming(\"Benchmark execution took\", getElapsed(benchStart));\n sendAndExit({ type: \"result\", results }, 0);\n }\n } catch (error) {\n sendAndExit(createErrorMessage(error), 1);\n }\n});\n\n// Exit after 5 minutes to prevent zombie processes\nsetTimeout(() => {\n console.error(\"WorkerScript: Maximum lifetime exceeded, exiting\");\n process.exit(1);\n}, maxLifetime);\n\nprocess.stdin.pause();\n\n/** Log timing with consistent format */\nconst logTiming = debugWorkerTiming ? _logTiming : () => {};\nfunction _logTiming(operation: string, duration?: number) {\n if (duration === undefined) {\n console.log(`[Worker] ${operation}`);\n } else {\n console.log(`[Worker] ${operation} ${duration.toFixed(1)}ms`);\n }\n}\n\n/** Send message and exit with duration log */\nfunction sendAndExit(msg: ResultMessage | ErrorMessage, exitCode: number) {\n process.send!(msg, undefined, undefined, (err: Error | null): void => {\n if (err) {\n const kind = msg.type === \"result\" ? \"results\" : \"error message\";\n console.error(`[Worker] Error sending ${kind}:`, err);\n }\n const suffix = exitCode === 0 ? \"\" : \" (error)\";\n logTiming(`Total worker duration${suffix}:`, getElapsed(workerStartTime));\n process.exit(exitCode);\n });\n}\n\ninterface BenchmarkImportResult {\n fn: BenchmarkFunction;\n params: unknown;\n}\n\n/** Resolve benchmark function from message (variant dir, module path, or fnCode) */\nasync function resolveBenchmarkFn(\n message: RunMessage,\n): Promise<BenchmarkImportResult> {\n if (message.variantDir && message.variantId) {\n return importVariantModule(message);\n }\n if (message.modulePath) {\n return importBenchmarkWithSetup(message);\n }\n return { fn: reconstructFunction(message.fnCode!), params: message.params };\n}\n\n/** Import variant from directory and prepare benchmark function */\nasync function importVariantModule(\n message: RunMessage,\n): Promise<BenchmarkImportResult> {\n const { variantDir, variantId, caseId, casesModule } = message;\n let { caseData } = message;\n const moduleUrl = variantModuleUrl(variantDir!, variantId!);\n logTiming(`Importing variant ${variantId} from ${variantDir}`);\n\n if (casesModule && caseId) {\n caseData = (await loadCaseFromModule(casesModule, caseId)).data;\n }\n\n const module = await import(moduleUrl);\n const { setup, run } = module;\n\n if (typeof run !== \"function\") {\n throw new Error(`Variant '${variantId}' must export 'run' function`);\n }\n\n // Stateful variant: setup returns state, run receives state\n if (typeof setup === \"function\") {\n logTiming(`Calling setup for ${variantId}`);\n const state = await setup(caseData);\n return { fn: () => run(state), params: undefined };\n }\n\n // Stateless variant: run receives caseData directly\n return { fn: () => run(caseData), params: undefined };\n}\n\n/** Load case data from a cases module */\nasync function loadCaseFromModule(\n casesModuleUrl: string,\n caseId: string,\n): Promise<{ data: unknown; metadata?: Record<string, unknown> }> {\n logTiming(`Loading case '${caseId}' from ${casesModuleUrl}`);\n const module = await import(casesModuleUrl);\n if (typeof module.loadCase === \"function\") {\n return module.loadCase(caseId);\n }\n return { data: caseId };\n}\n\n/** Import benchmark function and optionally run setup */\nasync function importBenchmarkWithSetup(\n message: RunMessage,\n): Promise<BenchmarkImportResult> {\n const { modulePath, exportName, setupExportName, params } = message;\n logTiming(\n `Importing from ${modulePath}${exportName ? ` (${exportName})` : \"\"}`,\n );\n const module = await import(modulePath!);\n\n const fn = getModuleExport(module, exportName, modulePath!);\n\n if (setupExportName) {\n logTiming(`Calling setup: ${setupExportName}`);\n const setupFn = getModuleExport(module, setupExportName, modulePath!);\n const setupResult = await setupFn(params);\n return { fn, params: setupResult };\n }\n\n return { fn, params };\n}\n\n/** Get named or default export from module */\nfunction getModuleExport(\n module: any,\n exportName: string | undefined,\n modulePath: string,\n): BenchmarkFunction {\n const fn = exportName ? module[exportName] : module.default || module;\n if (typeof fn !== \"function\") {\n const name = exportName || \"default\";\n throw new Error(`Export '${name}' from ${modulePath} is not a function`);\n }\n return fn;\n}\n\n/** Reconstruct function from string code */\nfunction reconstructFunction(fnCode: string): BenchmarkFunction {\n // biome-ignore lint/security/noGlobalEval: Necessary for worker process isolation, code is from trusted source\n const fn = eval(`(${fnCode})`); // eslint-disable-line no-eval\n if (typeof fn !== \"function\") {\n throw new Error(\"Reconstructed code is not a function\");\n }\n return fn;\n}\n\n/** Create error message from exception */\nfunction createErrorMessage(error: unknown): ErrorMessage {\n return {\n type: \"error\",\n error: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"WorkerScript.mjs","names":[],"sources":["../../src/runners/WorkerScript.ts"],"sourcesContent":["#!/usr/bin/env node\nimport type { BenchmarkFunction, BenchmarkSpec } from \"../Benchmark.ts\";\nimport type { HeapProfile } from \"../heap-sample/HeapSampler.ts\";\nimport type { MeasuredResults } from \"../MeasuredResults.ts\";\nimport { variantModuleUrl } from \"../matrix/VariantLoader.ts\";\nimport {\n type AdaptiveOptions,\n createAdaptiveWrapper,\n} from \"./AdaptiveWrapper.ts\";\nimport type { RunnerOptions } from \"./BenchRunner.ts\";\nimport { createRunner, type KnownRunner } from \"./CreateRunner.ts\";\nimport { debugWorkerTiming, getElapsed, getPerfNow } from \"./TimingUtils.ts\"; // 5 minutes\n\n/** Message sent to worker process to start a benchmark run. */\nexport interface RunMessage {\n type: \"run\";\n spec: BenchmarkSpec;\n runnerName: KnownRunner;\n options: RunnerOptions;\n fnCode?: string; // Made optional - either fnCode or modulePath is required\n modulePath?: string; // Path to module for dynamic import\n exportName?: string; // Export name from module\n setupExportName?: string; // Setup function export name - called once, result passed to fn\n params?: unknown;\n // Variant directory mode (BenchMatrix)\n variantDir?: string; // Directory URL containing variant .ts files\n variantId?: string; // Variant filename (without .ts)\n caseData?: unknown; // Data to pass to variant\n caseId?: string; // Case identifier\n casesModule?: string; // URL to cases module (exports cases[] and loadCase())\n}\n\n/** Message returned from worker process with benchmark results. */\nexport interface ResultMessage {\n type: \"result\";\n results: MeasuredResults[];\n heapProfile?: HeapProfile;\n}\n\n/** Message returned from worker process when benchmark fails. */\nexport interface ErrorMessage {\n type: \"error\";\n error: string;\n stack?: string;\n}\n\nexport type WorkerMessage = RunMessage | ResultMessage | ErrorMessage;\n\ninterface BenchmarkImportResult {\n fn: BenchmarkFunction;\n params: unknown;\n}\n\nconst workerStartTime = getPerfNow();\nconst maxLifetime = 5 * 60 * 1000;\n\n/** Log timing with consistent format */\nconst logTiming = debugWorkerTiming ? _logTiming : () => {};\nfunction _logTiming(operation: string, duration?: number) {\n if (duration === undefined) {\n console.log(`[Worker] ${operation}`);\n } else {\n console.log(`[Worker] ${operation} ${duration.toFixed(1)}ms`);\n }\n}\n\n/** Send message and exit with duration log */\nfunction sendAndExit(msg: ResultMessage | ErrorMessage, exitCode: number) {\n process.send!(msg, undefined, undefined, (err: Error | null): void => {\n if (err) {\n const kind = msg.type === \"result\" ? \"results\" : \"error message\";\n console.error(`[Worker] Error sending ${kind}:`, err);\n }\n const suffix = exitCode === 0 ? \"\" : \" (error)\";\n logTiming(`Total worker duration${suffix}:`, getElapsed(workerStartTime));\n process.exit(exitCode);\n });\n}\n\n/** Resolve benchmark function from message (variant dir, module path, or fnCode) */\nasync function resolveBenchmarkFn(\n message: RunMessage,\n): Promise<BenchmarkImportResult> {\n if (message.variantDir && message.variantId) {\n return importVariantModule(message);\n }\n if (message.modulePath) {\n return importBenchmarkWithSetup(message);\n }\n return { fn: reconstructFunction(message.fnCode!), params: message.params };\n}\n\n/** Import variant from directory and prepare benchmark function */\nasync function importVariantModule(\n message: RunMessage,\n): Promise<BenchmarkImportResult> {\n const { variantDir, variantId, caseId, casesModule } = message;\n let { caseData } = message;\n const moduleUrl = variantModuleUrl(variantDir!, variantId!);\n logTiming(`Importing variant ${variantId} from ${variantDir}`);\n\n if (casesModule && caseId) {\n caseData = (await loadCaseFromModule(casesModule, caseId)).data;\n }\n\n const module = await import(moduleUrl);\n const { setup, run } = module;\n\n if (typeof run !== \"function\") {\n throw new Error(`Variant '${variantId}' must export 'run' function`);\n }\n\n // Stateful variant: setup returns state, run receives state\n if (typeof setup === \"function\") {\n logTiming(`Calling setup for ${variantId}`);\n const state = await setup(caseData);\n return { fn: () => run(state), params: undefined };\n }\n\n // Stateless variant: run receives caseData directly\n return { fn: () => run(caseData), params: undefined };\n}\n\n/** Import benchmark function and optionally run setup */\nasync function importBenchmarkWithSetup(\n message: RunMessage,\n): Promise<BenchmarkImportResult> {\n const { modulePath, exportName, setupExportName, params } = message;\n logTiming(\n `Importing from ${modulePath}${exportName ? ` (${exportName})` : \"\"}`,\n );\n const module = await import(modulePath!);\n\n const fn = getModuleExport(module, exportName, modulePath!);\n\n if (setupExportName) {\n logTiming(`Calling setup: ${setupExportName}`);\n const setupFn = getModuleExport(module, setupExportName, modulePath!);\n const setupResult = await setupFn(params);\n return { fn, params: setupResult };\n }\n\n return { fn, params };\n}\n\n/** Reconstruct function from string code */\nfunction reconstructFunction(fnCode: string): BenchmarkFunction {\n // biome-ignore lint/security/noGlobalEval: Necessary for worker process isolation, code is from trusted source\n const fn = eval(`(${fnCode})`); // eslint-disable-line no-eval\n if (typeof fn !== \"function\") {\n throw new Error(\"Reconstructed code is not a function\");\n }\n return fn;\n}\n\n/** Load case data from a cases module */\nasync function loadCaseFromModule(\n casesModuleUrl: string,\n caseId: string,\n): Promise<{ data: unknown; metadata?: Record<string, unknown> }> {\n logTiming(`Loading case '${caseId}' from ${casesModuleUrl}`);\n const module = await import(casesModuleUrl);\n if (typeof module.loadCase === \"function\") {\n return module.loadCase(caseId);\n }\n return { data: caseId };\n}\n\n/** Get named or default export from module */\nfunction getModuleExport(\n module: any,\n exportName: string | undefined,\n modulePath: string,\n): BenchmarkFunction {\n const fn = exportName ? module[exportName] : module.default || module;\n if (typeof fn !== \"function\") {\n const name = exportName || \"default\";\n throw new Error(`Export '${name}' from ${modulePath} is not a function`);\n }\n return fn;\n}\n\n/** Create error message from exception */\nfunction createErrorMessage(error: unknown): ErrorMessage {\n return {\n type: \"error\",\n error: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined,\n };\n}\n\n/**\n * Worker process for isolated benchmark execution.\n * Uses eval() safely in isolated child process with trusted code.\n */\nprocess.on(\"message\", async (message: RunMessage) => {\n if (message.type !== \"run\") return;\n\n logTiming(`Processing ${message.spec.name} with ${message.runnerName}`);\n\n try {\n const start = getPerfNow();\n const baseRunner = await createRunner(message.runnerName);\n\n const runner = (message.options as any).adaptive\n ? createAdaptiveWrapper(baseRunner, message.options as AdaptiveOptions)\n : baseRunner;\n\n logTiming(\"Runner created in\", getElapsed(start));\n\n const benchStart = getPerfNow();\n\n // Run with heap sampling if enabled (covers module import + execution)\n if (message.options.heapSample) {\n const { withHeapSampling } = await import(\n \"../heap-sample/HeapSampler.ts\"\n );\n const heapOpts = {\n samplingInterval: message.options.heapInterval,\n stackDepth: message.options.heapDepth,\n };\n const { result: results, profile: heapProfile } = await withHeapSampling(\n heapOpts,\n async () => {\n const { fn, params } = await resolveBenchmarkFn(message);\n return runner.runBench(\n { ...message.spec, fn },\n message.options,\n params,\n );\n },\n );\n logTiming(\"Benchmark execution took\", getElapsed(benchStart));\n sendAndExit({ type: \"result\", results, heapProfile }, 0);\n } else {\n const { fn, params } = await resolveBenchmarkFn(message);\n const results = await runner.runBench(\n { ...message.spec, fn },\n message.options,\n params,\n );\n logTiming(\"Benchmark execution took\", getElapsed(benchStart));\n sendAndExit({ type: \"result\", results }, 0);\n }\n } catch (error) {\n sendAndExit(createErrorMessage(error), 1);\n }\n});\n\n// Exit after 5 minutes to prevent zombie processes\nsetTimeout(() => {\n console.error(\"WorkerScript: Maximum lifetime exceeded, exiting\");\n process.exit(1);\n}, maxLifetime);\n\nprocess.stdin.pause();\n"],"mappings":"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"}
|