@fraczak/k 7.0.0 → 7.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +50 -0
- package/DOCS/BIG_PICTURE.md +5 -5
- package/DOCS/FILE_FORMATS.md +5 -5
- package/DOCS/OBJECT_FILE_AND_PATTERN.md +9 -9
- package/DOCS/PATTERNS.md +3 -3
- package/DOCS/PROFILING_AND_TRACING.md +60 -72
- package/DOCS/REPL.md +3 -3
- package/DOCS/book/03-types-as-finite-automata.md +4 -4
- package/DOCS/book/06-values-in-memory.md +9 -9
- package/DOCS/book/08-operational-semantics-and-execution.md +1 -1
- package/DOCS/book/09-compiler-architecture.md +2 -2
- package/DOCS/book/10-from-ast-to-intermediate-representation.md +2 -2
- package/DOCS/book/14-canonical-serialization.md +1 -1
- package/Examples/arithmetics.k +2 -2
- package/Examples/bnat.k +1 -1
- package/Examples/karatsuba-mult.k +2 -2
- package/README.md +3 -8
- package/backend-api.mjs +9 -1
- package/backends/llvm/runtime/krt.c +33 -0
- package/backends/llvm/runtime/krt.h +1 -0
- package/backends/llvm/scripts/run-tests.mjs +5 -0
- package/backends/llvm/src/executable.mjs +4 -4
- package/backends/llvm/src/kvm2llvm.mjs +42 -19
- package/backends/llvm/src/llvm.mjs +30 -36
- package/backends/llvm/tests/integration.sh +2 -2
- package/backends/llvm/tests/perf-support.mjs +1 -55
- package/backends/llvm/tests/test-llvm.mjs +3 -3
- package/backends/llvm/tests/test-open-product-misalignment.mjs +140 -0
- package/backends/wasm/demo_ieee.mjs +2 -3
- package/backends/wasm/runtime.wat +157 -5
- package/backends/wasm/src/kvm2wasm.mjs +57 -34
- package/backends/wasm/src/wasm.mjs +40 -18
- package/backends/wasm/tests/perf-support.mjs +4 -4
- package/backends/wasm/tests/test-wasm.mjs +13 -14
- package/browser/vfs-data.mjs +6 -6
- package/core.k +8 -8
- package/harness/format.mjs +2 -2
- package/harness/registry.mjs +0 -2
- package/kir.mjs +299 -17
- package/package.json +1 -5
- package/scripts/benchmark-idempotency.mjs +12 -8
- package/scripts/benchmark.mjs +5 -9
- package/scripts/build-repl-html.mjs +2 -2
- package/scripts/perf-ieee.mjs +17 -118
- package/scripts/perf-int.mjs +313 -318
- package/scripts/perf-multiplication.mjs +10 -20
- package/scripts/perf-poly.mjs +29 -183
- package/tests/code-derivation/pattern-envelope.mjs +4 -4
- package/tests/test-kvm-polymorphic.mjs +2 -2
- package/tests/test-repl-html.mjs +15 -0
- package/tests/test-repl.mjs +10 -0
- package/backends/arm64/README.md +0 -103
- package/backends/arm64/bin/k-arm64-compile.mjs +0 -93
- package/backends/arm64/bin/k-arm64-run.mjs +0 -58
- package/backends/arm64/bin/k-arm64.mjs +0 -88
- package/backends/arm64/scripts/run-tests.mjs +0 -57
- package/backends/arm64/src/arm64.mjs +0 -229
- package/backends/arm64/src/cli.mjs +0 -155
- package/backends/arm64/src/kvm2arm64.mjs +0 -906
- package/backends/arm64/src/runtime.c +0 -744
- package/backends/arm64/tests/integration.sh +0 -112
- package/backends/arm64/tests/test-arm64.mjs +0 -240
- package/harness/adapters/arm64.mjs +0 -105
package/scripts/perf-int.mjs
CHANGED
|
@@ -23,8 +23,6 @@ import {
|
|
|
23
23
|
csvEnv,
|
|
24
24
|
formatTiming,
|
|
25
25
|
createLLVMRunner,
|
|
26
|
-
createARM64Runner,
|
|
27
|
-
arm64LaneName,
|
|
28
26
|
llvmLaneName,
|
|
29
27
|
makeCacheDir,
|
|
30
28
|
parseNonNegativeIntEnv,
|
|
@@ -53,7 +51,6 @@ import {
|
|
|
53
51
|
writeValueToArena
|
|
54
52
|
} from "../backends/wasm/tests/perf-support.mjs";
|
|
55
53
|
|
|
56
|
-
import { compileARM64ArtifactFromObject } from "../backends/arm64/src/arm64.mjs";
|
|
57
54
|
import { compileWasmArtifactFromObject } from "../backends/wasm/src/wasm.mjs";
|
|
58
55
|
|
|
59
56
|
function printHelp() {
|
|
@@ -61,37 +58,31 @@ function printHelp() {
|
|
|
61
58
|
|
|
62
59
|
Big Integer Arithmetic Performance Benchmark Harness.
|
|
63
60
|
Benchmarks plus, minus, times, and squaring scale on arithmetics.k
|
|
64
|
-
across Native JS, kVM interpreter, LLVM,
|
|
61
|
+
across Native JS, kVM interpreter, LLVM, and Wasm backends.
|
|
65
62
|
|
|
66
63
|
Options:
|
|
67
64
|
--wasm-in-process
|
|
68
65
|
Run WebAssembly in-process instead of persistent subprocess runner.
|
|
66
|
+
--squaring-llvm-only
|
|
67
|
+
Run squaring benchmark on LLVM only.
|
|
69
68
|
-h, --help
|
|
70
69
|
Show this help message and exit.
|
|
71
70
|
|
|
72
71
|
Environment Variables:
|
|
73
72
|
BACKENDS
|
|
74
|
-
Target backend to run: 'llvm'
|
|
73
|
+
Target backend to run: 'llvm' or 'wasm'.
|
|
75
74
|
LLVM_ONLY
|
|
76
75
|
Run only LLVM backend ('1' to enable).
|
|
77
76
|
WASM_ONLY
|
|
78
77
|
Run only WebAssembly backend ('1' to enable).
|
|
79
|
-
ARM64_ONLY
|
|
80
|
-
Run only ARM64 backend ('1' to enable).
|
|
81
78
|
BACKENDS_ONLY
|
|
82
79
|
Skip JS and kVM interpreter baselines ('1' to enable).
|
|
80
|
+
SQUARING_LLVM_ONLY
|
|
81
|
+
Run squaring benchmark on LLVM only ('1' to enable).
|
|
83
82
|
INPUTS
|
|
84
83
|
Comma-separated integer strings to benchmark.
|
|
85
84
|
ITERATIONS
|
|
86
85
|
Measured iterations per benchmark (default: 3).
|
|
87
|
-
ARM64_WARMUP_ITERATIONS
|
|
88
|
-
Warmup iterations for ARM64 backend (default: 1).
|
|
89
|
-
ARM64_OPT
|
|
90
|
-
ARM64 backend optimization flags (default: -O2).
|
|
91
|
-
ARM64_SPAWN_PER_CALL
|
|
92
|
-
Spawn process per ARM64 call instead of persistent ('1' or '0').
|
|
93
|
-
ARM64_PIPELINE
|
|
94
|
-
Use persistent pipelined runner for ARM64 ('1' or '0').
|
|
95
86
|
LLVM_WARMUP_ITERATIONS
|
|
96
87
|
Warmup iterations for LLVM backend (default: 1).
|
|
97
88
|
LLVM_SPAWN_PER_CALL
|
|
@@ -125,15 +116,12 @@ if (process.argv.includes("-h") || process.argv.includes("--help")) {
|
|
|
125
116
|
|
|
126
117
|
// Backend selection
|
|
127
118
|
const backendsEnv = process.env.BACKENDS?.toLowerCase();
|
|
128
|
-
const llvmOnly = process.env.LLVM_ONLY === "1" || backendsEnv === "llvm";
|
|
129
|
-
const wasmOnly = process.env.WASM_ONLY === "1" || backendsEnv === "wasm";
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
const
|
|
133
|
-
const
|
|
134
|
-
const runLLVM = !wasmOnly && !arm64Only;
|
|
135
|
-
const runWasm = !llvmOnly && !arm64Only;
|
|
136
|
-
const runBaselines = !process.env.BACKENDS_ONLY && (process.env.LLVM_ONLY !== "1") && (process.env.WASM_ONLY !== "1") && (process.env.ARM64_ONLY !== "1");
|
|
119
|
+
const llvmOnly = process.env.LLVM_ONLY === "1" || backendsEnv === "llvm" || process.argv.includes("--llvm-only");
|
|
120
|
+
const wasmOnly = process.env.WASM_ONLY === "1" || backendsEnv === "wasm" || process.argv.includes("--wasm-only");
|
|
121
|
+
const runLLVM = !wasmOnly;
|
|
122
|
+
const runWasm = !llvmOnly;
|
|
123
|
+
const runBaselines = !process.env.BACKENDS_ONLY && (process.env.LLVM_ONLY !== "1") && (process.env.WASM_ONLY !== "1");
|
|
124
|
+
const squaringLlvmOnly = llvmOnly || process.env.SQUARING_LLVM_ONLY === "1" || process.argv.includes("--squaring-llvm-only");
|
|
137
125
|
|
|
138
126
|
const ops = ["plus", "minus", "times"];
|
|
139
127
|
const benchNames = Object.fromEntries(ops.map(op => [op, `bench_${op}`]));
|
|
@@ -143,9 +131,6 @@ const inputTexts = csvEnv(
|
|
|
143
131
|
);
|
|
144
132
|
const iterations = parsePositiveIntEnv("ITERATIONS", 3);
|
|
145
133
|
|
|
146
|
-
// ARM64 options
|
|
147
|
-
const arm64WarmupIterations = parseNonNegativeIntEnv("ARM64_WARMUP_ITERATIONS", 1);
|
|
148
|
-
const arm64OptLevel = process.env.ARM64_OPT || "-O2";
|
|
149
134
|
if (!process.env.K_ARENA_CAPACITY_MB && !process.env.K_ARENA_MB) {
|
|
150
135
|
process.env.K_ARENA_CAPACITY_MB = "4096";
|
|
151
136
|
}
|
|
@@ -175,7 +160,7 @@ for (const op of ops) {
|
|
|
175
160
|
}
|
|
176
161
|
|
|
177
162
|
function intValue(text) {
|
|
178
|
-
return
|
|
163
|
+
return parseIntValue(text);
|
|
179
164
|
}
|
|
180
165
|
|
|
181
166
|
console.log("==> Preparing relations");
|
|
@@ -315,39 +300,6 @@ function createWasmRunner(testSuite) {
|
|
|
315
300
|
};
|
|
316
301
|
}
|
|
317
302
|
|
|
318
|
-
// Setup ARM64
|
|
319
|
-
const arm64Exes = new Map();
|
|
320
|
-
if (runARM64) {
|
|
321
|
-
console.log(`==> Compiling Linux ARM64 executables (${arm64OptLevel})...`);
|
|
322
|
-
for (const op of ops) {
|
|
323
|
-
const relation = relations[op];
|
|
324
|
-
try {
|
|
325
|
-
const exePath = path.join(cacheDir, `perf_int_arm64_${op}`);
|
|
326
|
-
compileARM64ArtifactFromObject(relation.object, {
|
|
327
|
-
entry: "__main__",
|
|
328
|
-
outputPath: exePath,
|
|
329
|
-
optLevel: arm64OptLevel
|
|
330
|
-
});
|
|
331
|
-
arm64Exes.set(op, { status: "ok", exePath });
|
|
332
|
-
} catch (error) {
|
|
333
|
-
arm64Exes.set(op, {
|
|
334
|
-
status: "failed",
|
|
335
|
-
error: error.stack || error.message || String(error)
|
|
336
|
-
});
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
for (const tc of testSuite) {
|
|
340
|
-
tc.arm64 = arm64Exes.get(tc.op);
|
|
341
|
-
}
|
|
342
|
-
const failedOps = [...arm64Exes.entries()].filter(([_, v]) => v.status !== "ok");
|
|
343
|
-
if (failedOps.length > 0) {
|
|
344
|
-
console.log("Linux ARM64 compilation failures:");
|
|
345
|
-
for (const [op, res] of failedOps) {
|
|
346
|
-
console.log(` ${op}: ${res.error.split("\n")[0]}`);
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
|
|
351
303
|
function printBenchmarkDescription() {
|
|
352
304
|
const lanes = [];
|
|
353
305
|
if (runBaselines) {
|
|
@@ -357,7 +309,6 @@ function printBenchmarkDescription() {
|
|
|
357
309
|
}
|
|
358
310
|
if (runLLVM) lanes.push(llvmLaneName());
|
|
359
311
|
if (runWasm) lanes.push(wasmLaneName());
|
|
360
|
-
if (runARM64) lanes.push(arm64LaneName(arm64OptLevel));
|
|
361
312
|
|
|
362
313
|
console.log("==> Benchmark description");
|
|
363
314
|
console.log(" source: @fraczak/k/Examples/arithmetics.k");
|
|
@@ -374,12 +325,6 @@ function printBenchmarkDescription() {
|
|
|
374
325
|
console.log(` wasm warmup iterations: ${wasmWarmupIterations}`);
|
|
375
326
|
if (!wasmPipe) console.log(` wasm arena reset: ${wasmReset ? "yes" : "no"}`);
|
|
376
327
|
}
|
|
377
|
-
if (runARM64) {
|
|
378
|
-
console.log(` arm64-ready cases: ${testSuite.filter(tc => tc.arm64?.status === "ok").length}`);
|
|
379
|
-
console.log(` arm64 warmup iterations: ${arm64WarmupIterations}`);
|
|
380
|
-
console.log(` arm64 opt level: ${arm64OptLevel}`);
|
|
381
|
-
console.log(` arm64 runner mode: ${process.env.ARM64_SPAWN_PER_CALL === "1" ? "spawn per call" : "persistent"}`);
|
|
382
|
-
}
|
|
383
328
|
console.log(` iterations: ${iterations}`);
|
|
384
329
|
console.log(` benchmark lanes: ${lanes.join("; ")}`);
|
|
385
330
|
console.log(" conformance: all outputs are compared to native expected values");
|
|
@@ -458,26 +403,6 @@ if (runWasm) {
|
|
|
458
403
|
}
|
|
459
404
|
}
|
|
460
405
|
|
|
461
|
-
// 4. Linux ARM64 lane
|
|
462
|
-
let arm64Result = null;
|
|
463
|
-
if (runARM64) {
|
|
464
|
-
const arm64Cases = testSuite.filter(tc => tc.arm64?.status === "ok");
|
|
465
|
-
if (arm64Cases.length === testSuite.length) {
|
|
466
|
-
const arm64Runner = createARM64Runner(testSuite);
|
|
467
|
-
try {
|
|
468
|
-
if (arm64WarmupIterations > 0) {
|
|
469
|
-
console.log(`==> Warming Linux ARM64 (${arm64WarmupIterations} iterations)...`);
|
|
470
|
-
await arm64Runner.run(arm64WarmupIterations);
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
console.log(`==> Running Linux ARM64 (${iterations} iterations)...`);
|
|
474
|
-
arm64Result = await arm64Runner.run(iterations);
|
|
475
|
-
} finally {
|
|
476
|
-
arm64Runner.close();
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
|
|
481
406
|
console.log("\n=================== INTEGER BENCHMARK RESULTS ===================");
|
|
482
407
|
console.log(`Operation calls per iteration: ${testSuite.length}`);
|
|
483
408
|
console.log(`Total requested operation calls: ${iterations * testSuite.length}`);
|
|
@@ -494,10 +419,6 @@ if (runLLVM) {
|
|
|
494
419
|
if (runWasm) {
|
|
495
420
|
console.log(`${laneIndex++}. ${wasmLaneName().padEnd(29)} ${formatTiming(wasmResult)}`);
|
|
496
421
|
}
|
|
497
|
-
if (runARM64) {
|
|
498
|
-
const timingStr = arm64Result ? formatTiming(arm64Result) : "compile failed";
|
|
499
|
-
console.log(`${laneIndex++}. ${arm64LaneName(arm64OptLevel).padEnd(29)} ${timingStr}`);
|
|
500
|
-
}
|
|
501
422
|
console.log("=================================================================\n");
|
|
502
423
|
|
|
503
424
|
// Conformance Validation
|
|
@@ -563,23 +484,6 @@ for (const tc of testSuite) {
|
|
|
563
484
|
}
|
|
564
485
|
}
|
|
565
486
|
|
|
566
|
-
if (runARM64) {
|
|
567
|
-
if (tc.arm64?.status !== "ok") {
|
|
568
|
-
tc.arm64Conformance = "compile-failed";
|
|
569
|
-
} else {
|
|
570
|
-
try {
|
|
571
|
-
const outputWire = await runExecutable(tc.arm64.exePath, tc.inputWire);
|
|
572
|
-
const actual = decodeWire(outputWire).value;
|
|
573
|
-
assert.deepEqual(toPlainObject(actual), toPlainObject(tc.expected));
|
|
574
|
-
tc.arm64Conformance = "ok";
|
|
575
|
-
} catch (error) {
|
|
576
|
-
tc.arm64Conformance = "failed";
|
|
577
|
-
tc.arm64.error = error.stack || error.message || String(error);
|
|
578
|
-
console.log(`Linux ARM64 conformance failure for ${tc.op}(${tc.label}):`);
|
|
579
|
-
console.log(tc.arm64.error.split("\n").slice(0, 8).join("\n"));
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
487
|
}
|
|
584
488
|
|
|
585
489
|
const validationSummary = [];
|
|
@@ -592,20 +496,15 @@ if (runWasm) {
|
|
|
592
496
|
const wasmOk = testSuite.filter(tc => tc.wasmConformance === "ok").length;
|
|
593
497
|
validationSummary.push(`Wasm ${wasmOk}/${testSuite.length}`);
|
|
594
498
|
}
|
|
595
|
-
if (runARM64) {
|
|
596
|
-
const arm64Ok = testSuite.filter(tc => tc.arm64Conformance === "ok").length;
|
|
597
|
-
validationSummary.push(`Linux ARM64 ${arm64Ok}/${testSuite.length}`);
|
|
598
|
-
}
|
|
599
499
|
|
|
600
500
|
console.log(`Conformance validation: ${validationSummary.join(", ")} cases match expected values.`);
|
|
601
501
|
if (runLLVM && shouldStrictFail(testSuite)) process.exitCode = 1;
|
|
602
502
|
if (runWasm && testSuite.some(tc => tc.wasmConformance !== "ok")) process.exitCode = 1;
|
|
603
|
-
if (runARM64 && testSuite.some(tc => tc.arm64Conformance !== "ok")) process.exitCode = 1;
|
|
604
503
|
|
|
605
|
-
//
|
|
504
|
+
// =======================================================================
|
|
606
505
|
// Squaring Scale & Memory Profiling Benchmark
|
|
607
|
-
//
|
|
608
|
-
//
|
|
506
|
+
// Expressions: s = {()x,()y}times; and s = {()x,()y}karatsuba;
|
|
507
|
+
// =======================================================================
|
|
609
508
|
|
|
610
509
|
function countBitsValue(val) {
|
|
611
510
|
let count = 0;
|
|
@@ -644,189 +543,214 @@ const squaringInputText = process.env.SQUARING_INPUT || "987654321";
|
|
|
644
543
|
const squaringStepTimeoutMs = parsePositiveIntEnv("SQUARING_STEP_TIMEOUT_MS", 60000);
|
|
645
544
|
|
|
646
545
|
console.log("\n=================== SQUARING SCALE & MEMORY BENCHMARK ===================");
|
|
647
|
-
console.log(`
|
|
648
|
-
console.log(`Evaluation: ${squaringSteps} successive squarings doubling bit length each step`);
|
|
546
|
+
console.log(`Algorithms: times vs karatsuba (${squaringSteps} successive squarings doubling bit length)`);
|
|
649
547
|
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
const
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
});
|
|
548
|
+
// Load and initialize Karatsuba multiplication relation
|
|
549
|
+
const karatsubaPath = fileURLToPath(import.meta.resolve("@fraczak/k/Examples/karatsuba-mult.k"));
|
|
550
|
+
const karatsubaSource = fs.readFileSync(karatsubaPath, "utf8");
|
|
551
|
+
const karatsubaLibrarySource = karatsubaSource.replace(/\s*\(\)\s*$/, "\n");
|
|
552
|
+
const stateKara = createState();
|
|
553
|
+
await evaluateInput(`:load ${karatsubaPath}`, stateKara);
|
|
554
|
+
codes.load(stateKara.codes);
|
|
656
555
|
|
|
657
556
|
const sInitialVal = intValue(squaringInputText);
|
|
658
557
|
const { inputWire: sInitWire, inputPattern: sInputPattern } = wireInput(sInitialVal);
|
|
659
558
|
|
|
559
|
+
const squaringAlgos = [
|
|
560
|
+
{
|
|
561
|
+
id: "times",
|
|
562
|
+
name: "times",
|
|
563
|
+
title: "Long Multiplication (times)",
|
|
564
|
+
expr: "s = {()x,()y}times;",
|
|
565
|
+
librarySource: arithmeticsLibrarySource,
|
|
566
|
+
sourceLabel: "@fraczak/k/Examples/arithmetics.k#s",
|
|
567
|
+
state: state,
|
|
568
|
+
relName: "s"
|
|
569
|
+
},
|
|
570
|
+
{
|
|
571
|
+
id: "karatsuba",
|
|
572
|
+
name: "karatsuba",
|
|
573
|
+
title: "Karatsuba Multiplication (karatsuba)",
|
|
574
|
+
expr: "s = {()x,()y}karatsuba;",
|
|
575
|
+
librarySource: karatsubaLibrarySource,
|
|
576
|
+
sourceLabel: "@fraczak/k/Examples/karatsuba-mult.k#s",
|
|
577
|
+
state: stateKara,
|
|
578
|
+
relName: "s"
|
|
579
|
+
}
|
|
580
|
+
];
|
|
581
|
+
|
|
660
582
|
// Build executables for persistent servers
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
inputPattern: sInputPattern,
|
|
668
|
-
cacheDir,
|
|
669
|
-
sourceLabel: "@fraczak/k/Examples/arithmetics.k#s"
|
|
583
|
+
for (const algo of squaringAlgos) {
|
|
584
|
+
await evaluateInput(algo.expr, algo.state);
|
|
585
|
+
codes.load(algo.state.codes);
|
|
586
|
+
algo.rel = prepareRelation(algo.state, algo.relName, {
|
|
587
|
+
source: `${algo.librarySource}\n${algo.expr}\n${algo.relName}`,
|
|
588
|
+
sourceLabel: algo.sourceLabel
|
|
670
589
|
});
|
|
671
|
-
if (sLlvmCompiled.status === "ok") {
|
|
672
|
-
sLlvmServer = new PersistentExecutable(sLlvmCompiled.exePath);
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
590
|
|
|
676
|
-
|
|
677
|
-
if (
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
591
|
+
algo.llvmServer = null;
|
|
592
|
+
if (runLLVM) {
|
|
593
|
+
const sLlvmCompiled = tryCompileCase({
|
|
594
|
+
object: algo.rel.object,
|
|
595
|
+
relationName: algo.rel.relationName,
|
|
596
|
+
relHash: algo.rel.relHash,
|
|
597
|
+
inputPattern: sInputPattern,
|
|
598
|
+
cacheDir,
|
|
599
|
+
sourceLabel: algo.sourceLabel
|
|
600
|
+
});
|
|
601
|
+
if (sLlvmCompiled.status === "ok") {
|
|
602
|
+
algo.llvmServer = new PersistentExecutable(sLlvmCompiled.exePath);
|
|
603
|
+
}
|
|
688
604
|
}
|
|
689
|
-
}
|
|
690
605
|
|
|
691
|
-
|
|
692
|
-
if (
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
entry: "__main__"
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
606
|
+
algo.wasmServer = null;
|
|
607
|
+
if (runWasm && !squaringLlvmOnly) {
|
|
608
|
+
try {
|
|
609
|
+
const sWasmPath = path.join(cacheDir, `perf_int_wasm_${algo.id}.wasm`);
|
|
610
|
+
const sWasmExe = path.join(cacheDir, `perf_int_wasm_${algo.id}.exe`);
|
|
611
|
+
const wasmBuf = await compileWasmArtifactFromObject(algo.rel.object, { entry: "__main__" });
|
|
612
|
+
fs.writeFileSync(sWasmPath, wasmBuf);
|
|
613
|
+
const runBin = fileURLToPath(import.meta.resolve("../backends/wasm/bin/k-wasm-run.mjs"));
|
|
614
|
+
fs.writeFileSync(sWasmExe, `#!/bin/sh\nexec node "${runBin}" "${sWasmPath}" "$@"\n`, { mode: 0o755 });
|
|
615
|
+
algo.wasmServer = new PersistentExecutable(sWasmExe);
|
|
616
|
+
} catch (e) {
|
|
617
|
+
console.log(`Failed to build Wasm executable for '${algo.id}':`, e.message);
|
|
618
|
+
}
|
|
703
619
|
}
|
|
620
|
+
|
|
621
|
+
algo.results = {};
|
|
704
622
|
}
|
|
705
623
|
|
|
706
624
|
const squaringLanes = [];
|
|
707
|
-
if (runBaselines) {
|
|
625
|
+
if (runBaselines && !squaringLlvmOnly) {
|
|
708
626
|
squaringLanes.push({ id: "jsAware", name: "Native JS (Aware)", type: "jsAware", memLabel: "heap" });
|
|
709
627
|
squaringLanes.push({ id: "jsFree", name: "Native JS (Free)", type: "jsFree", memLabel: "heap" });
|
|
710
628
|
squaringLanes.push({ id: "kvm", name: "kVM (Env-Free)", type: "kvm", memLabel: "heap" });
|
|
711
629
|
}
|
|
712
|
-
if (runLLVM
|
|
713
|
-
squaringLanes.push({ id: "llvm", name: llvmLaneName(), type: "persistent",
|
|
714
|
-
}
|
|
715
|
-
if (runWasm && sWasmServer) {
|
|
716
|
-
squaringLanes.push({ id: "wasm", name: wasmLaneName(), type: "persistent", server: sWasmServer, memLabel: "peak RSS" });
|
|
630
|
+
if (runLLVM) {
|
|
631
|
+
squaringLanes.push({ id: "llvm", name: llvmLaneName(), type: "persistent", memLabel: "peak RSS" });
|
|
717
632
|
}
|
|
718
|
-
if (
|
|
719
|
-
squaringLanes.push({ id: "
|
|
633
|
+
if (runWasm && !squaringLlvmOnly) {
|
|
634
|
+
squaringLanes.push({ id: "wasm", name: wasmLaneName(), type: "persistent", memLabel: "peak RSS" });
|
|
720
635
|
}
|
|
721
636
|
|
|
722
|
-
const
|
|
723
|
-
for (const lane of squaringLanes)
|
|
637
|
+
for (const algo of squaringAlgos) {
|
|
638
|
+
for (const lane of squaringLanes) {
|
|
639
|
+
algo.results[lane.id] = [];
|
|
640
|
+
}
|
|
641
|
+
}
|
|
724
642
|
const stepMetadata = [];
|
|
725
643
|
|
|
726
644
|
try {
|
|
727
|
-
// 1. Persistent child processes (
|
|
645
|
+
// 1. Persistent child processes (Wasm, LLVM)
|
|
728
646
|
for (const lane of squaringLanes.filter(l => l.type === "persistent")) {
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
647
|
+
for (const algo of squaringAlgos) {
|
|
648
|
+
const server = lane.id === "llvm" ? algo.llvmServer : algo.wasmServer;
|
|
649
|
+
if (!server) continue;
|
|
650
|
+
console.log(`==> Running ${lane.name} squaring [${algo.name}]...`);
|
|
651
|
+
let curWire = sInitWire;
|
|
652
|
+
for (let step = 1; step <= squaringSteps; step++) {
|
|
653
|
+
try {
|
|
654
|
+
const t0 = performance.now();
|
|
655
|
+
curWire = await server.request(curWire);
|
|
656
|
+
const t1 = performance.now();
|
|
657
|
+
const timeMs = t1 - t0;
|
|
658
|
+
const mem = getProcessMemory(server.child.pid);
|
|
659
|
+
algo.results[lane.id].push({ status: "ok", timeMs, memMb: mem.peakRssMb, wire: curWire });
|
|
660
|
+
if (!stepMetadata[step - 1]) {
|
|
661
|
+
stepMetadata[step - 1] = {
|
|
662
|
+
wireLen: curWire.length,
|
|
663
|
+
bits: null,
|
|
664
|
+
canonicalWire: curWire
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
if (timeMs > squaringStepTimeoutMs) break;
|
|
668
|
+
} catch (e) {
|
|
669
|
+
algo.results[lane.id].push({ status: "failed", error: e.message?.split("\n")[0] || String(e) });
|
|
670
|
+
break;
|
|
745
671
|
}
|
|
746
|
-
if (timeMs > squaringStepTimeoutMs) break;
|
|
747
|
-
} catch (e) {
|
|
748
|
-
squaringResults[lane.id].push({ status: "failed", error: e.message?.split("\n")[0] || String(e) });
|
|
749
|
-
break;
|
|
750
672
|
}
|
|
751
673
|
}
|
|
752
674
|
}
|
|
753
675
|
|
|
754
676
|
// 2. In-process baselines (JS Aware, JS Free, kVM)
|
|
755
|
-
if (runBaselines) {
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
stepMetadata[step - 1].bits
|
|
677
|
+
if (runBaselines && !squaringLlvmOnly) {
|
|
678
|
+
for (const algo of squaringAlgos) {
|
|
679
|
+
// JS Aware
|
|
680
|
+
console.log(`==> Running Native JS (Aware) squaring [${algo.name}]...`);
|
|
681
|
+
let curValAware = sInitialVal;
|
|
682
|
+
run.defs = algo.state;
|
|
683
|
+
for (let step = 1; step <= squaringSteps; step++) {
|
|
684
|
+
try {
|
|
685
|
+
const t0 = performance.now();
|
|
686
|
+
curValAware = run(codes.find, algo.rel.relDef.def, curValAware, algo.rel.relDef.typePatternGraph);
|
|
687
|
+
const t1 = performance.now();
|
|
688
|
+
const timeMs = t1 - t0;
|
|
689
|
+
const mem = getNodeMemory();
|
|
690
|
+
algo.results.jsAware.push({ status: "ok", timeMs, memMb: mem.heapMb, value: curValAware });
|
|
691
|
+
if (stepMetadata[step - 1] && stepMetadata[step - 1].bits == null) {
|
|
692
|
+
stepMetadata[step - 1].bits = countBitsValue(curValAware);
|
|
693
|
+
}
|
|
694
|
+
if (timeMs > squaringStepTimeoutMs) break;
|
|
695
|
+
} catch (e) {
|
|
696
|
+
algo.results.jsAware.push({ status: "failed", error: e.message?.split("\n")[0] || String(e) });
|
|
697
|
+
break;
|
|
770
698
|
}
|
|
771
|
-
if (timeMs > squaringStepTimeoutMs) break;
|
|
772
|
-
} catch (e) {
|
|
773
|
-
squaringResults.jsAware.push({ status: "failed", error: e.message?.split("\n")[0] || String(e) });
|
|
774
|
-
break;
|
|
775
699
|
}
|
|
776
|
-
}
|
|
777
700
|
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
701
|
+
// JS Free
|
|
702
|
+
console.log(`==> Running Native JS (Free) squaring [${algo.name}]...`);
|
|
703
|
+
let curValFree = sInitialVal;
|
|
704
|
+
run_converged.defs = algo.state;
|
|
705
|
+
for (let step = 1; step <= squaringSteps; step++) {
|
|
706
|
+
try {
|
|
707
|
+
const t0 = performance.now();
|
|
708
|
+
curValFree = run_converged(codes.find, algo.rel.relDef.def, curValFree, algo.rel.relDef.typePatternGraph);
|
|
709
|
+
const t1 = performance.now();
|
|
710
|
+
const timeMs = t1 - t0;
|
|
711
|
+
const mem = getNodeMemory();
|
|
712
|
+
algo.results.jsFree.push({ status: "ok", timeMs, memMb: mem.heapMb, value: curValFree });
|
|
713
|
+
if (stepMetadata[step - 1] && stepMetadata[step - 1].bits == null) {
|
|
714
|
+
stepMetadata[step - 1].bits = countBitsValue(curValFree);
|
|
715
|
+
}
|
|
716
|
+
if (timeMs > squaringStepTimeoutMs) break;
|
|
717
|
+
} catch (e) {
|
|
718
|
+
algo.results.jsFree.push({ status: "failed", error: e.message?.split("\n")[0] || String(e) });
|
|
719
|
+
break;
|
|
792
720
|
}
|
|
793
|
-
if (timeMs > squaringStepTimeoutMs) break;
|
|
794
|
-
} catch (e) {
|
|
795
|
-
squaringResults.jsFree.push({ status: "failed", error: e.message?.split("\n")[0] || String(e) });
|
|
796
|
-
break;
|
|
797
721
|
}
|
|
798
|
-
}
|
|
799
722
|
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
723
|
+
// kVM
|
|
724
|
+
console.log(`==> Running kVM Interpreter squaring [${algo.name}]...`);
|
|
725
|
+
let curValKvm = sInitialVal;
|
|
726
|
+
const kvmContext = { rels: algo.state.rels, findCode: codes.find, options: { envelopeFree: true } };
|
|
727
|
+
for (let step = 1; step <= squaringSteps; step++) {
|
|
728
|
+
try {
|
|
729
|
+
const t0 = performance.now();
|
|
730
|
+
curValKvm = executeKVM(algo.rel.kvmFunc, curValKvm, kvmContext);
|
|
731
|
+
const t1 = performance.now();
|
|
732
|
+
const timeMs = t1 - t0;
|
|
733
|
+
const mem = getNodeMemory();
|
|
734
|
+
algo.results.kvm.push({ status: "ok", timeMs, memMb: mem.heapMb, value: curValKvm });
|
|
735
|
+
if (stepMetadata[step - 1] && stepMetadata[step - 1].bits == null) {
|
|
736
|
+
stepMetadata[step - 1].bits = countBitsValue(curValKvm);
|
|
737
|
+
}
|
|
738
|
+
if (timeMs > squaringStepTimeoutMs) break;
|
|
739
|
+
} catch (e) {
|
|
740
|
+
algo.results.kvm.push({ status: "failed", error: e.message?.split("\n")[0] || String(e) });
|
|
741
|
+
break;
|
|
814
742
|
}
|
|
815
|
-
if (timeMs > squaringStepTimeoutMs) break;
|
|
816
|
-
} catch (e) {
|
|
817
|
-
squaringResults.kvm.push({ status: "failed", error: e.message?.split("\n")[0] || String(e) });
|
|
818
|
-
break;
|
|
819
743
|
}
|
|
820
744
|
}
|
|
821
745
|
}
|
|
822
|
-
|
|
823
746
|
} finally {
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
747
|
+
for (const algo of squaringAlgos) {
|
|
748
|
+
if (algo.wasmServer) algo.wasmServer.close();
|
|
749
|
+
if (algo.llvmServer) algo.llvmServer.close();
|
|
750
|
+
}
|
|
827
751
|
}
|
|
828
752
|
|
|
829
|
-
// For any remaining steps without bit counts, decode canonicalWire safely
|
|
753
|
+
// For any remaining steps without bit counts, decode canonicalWire safely
|
|
830
754
|
for (let step = 1; step <= squaringSteps; step++) {
|
|
831
755
|
const meta = stepMetadata[step - 1];
|
|
832
756
|
if (meta && meta.bits == null && meta.canonicalWire) {
|
|
@@ -838,87 +762,158 @@ for (let step = 1; step <= squaringSteps; step++) {
|
|
|
838
762
|
}
|
|
839
763
|
|
|
840
764
|
// Display results table
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
console.log(
|
|
848
|
-
console.log(
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
const
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
765
|
+
const sep130 = "=".repeat(130);
|
|
766
|
+
const line130 = "-".repeat(130);
|
|
767
|
+
|
|
768
|
+
if (squaringLanes.length === 1 && squaringLanes[0].id === "llvm") {
|
|
769
|
+
// LLVM Only side-by-side comparison table
|
|
770
|
+
console.log(`\n${sep130}`);
|
|
771
|
+
console.log("=================== SQUARING SCALE & ALGORITHM BENCHMARK (LLVM) ===================");
|
|
772
|
+
console.log(`Algorithms: times vs karatsuba on ${squaringLanes[0].name}`);
|
|
773
|
+
console.log(`Initial: ${squaringInputText} (${countBitsValue(sInitialVal)} bits)`);
|
|
774
|
+
console.log(line130);
|
|
775
|
+
|
|
776
|
+
const tAlgo = squaringAlgos[0];
|
|
777
|
+
const kAlgo = squaringAlgos[1];
|
|
778
|
+
const colW = [4, 6, 7, 34, 34, 15];
|
|
779
|
+
const hCols = [
|
|
780
|
+
"Step",
|
|
781
|
+
"Bits",
|
|
782
|
+
"Wire(B)",
|
|
783
|
+
"LLVM times (peak RSS)",
|
|
784
|
+
"LLVM karatsuba (peak RSS)",
|
|
785
|
+
"Speedup (T/K)"
|
|
856
786
|
];
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
}
|
|
872
|
-
|
|
787
|
+
console.log(hCols.map((h, i) => (i < 3 ? h.padStart(colW[i]) : h.padEnd(colW[i]))).join(" | "));
|
|
788
|
+
console.log(colW.map(w => "-".repeat(w)).join("-|-"));
|
|
789
|
+
|
|
790
|
+
for (let step = 1; step <= squaringSteps; step++) {
|
|
791
|
+
const meta = stepMetadata[step - 1] || { bits: "?", wireLen: "?" };
|
|
792
|
+
const tRes = tAlgo.results.llvm[step - 1];
|
|
793
|
+
const kRes = kAlgo.results.llvm[step - 1];
|
|
794
|
+
|
|
795
|
+
const formatCell = (res, w) => {
|
|
796
|
+
if (!res) return "-".padEnd(w);
|
|
797
|
+
if (res.status === "ok") {
|
|
798
|
+
const t = res.timeMs < 10 ? `${res.timeMs.toFixed(2)}ms` : `${res.timeMs.toFixed(1)}ms`;
|
|
799
|
+
const m = res.memMb != null ? `${res.memMb}MB` : "";
|
|
800
|
+
return `${t} / ${m}`.padEnd(w);
|
|
801
|
+
}
|
|
802
|
+
let errText = res.error || "error";
|
|
803
|
+
if (errText.includes("Maximum call stack")) errText = "Stack overflow (V8)";
|
|
804
|
+
else if (errText.includes("status null") || errText.includes("failed with status")) errText = "Arena limit reached";
|
|
805
|
+
return errText.slice(0, w - 1).padEnd(w);
|
|
806
|
+
};
|
|
807
|
+
|
|
808
|
+
let speedupStr = "-";
|
|
809
|
+
if (tRes?.status === "ok" && kRes?.status === "ok" && kRes.timeMs > 0) {
|
|
810
|
+
speedupStr = `${(tRes.timeMs / kRes.timeMs).toFixed(2)}x`;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
const row = [
|
|
814
|
+
String(step).padStart(colW[0]),
|
|
815
|
+
String(meta.bits ?? "?").padStart(colW[1]),
|
|
816
|
+
String(meta.wireLen ?? "?").padStart(colW[2]),
|
|
817
|
+
formatCell(tRes, colW[3]),
|
|
818
|
+
formatCell(kRes, colW[4]),
|
|
819
|
+
speedupStr.padEnd(colW[5])
|
|
820
|
+
];
|
|
821
|
+
console.log(row.join(" | "));
|
|
822
|
+
}
|
|
823
|
+
console.log(sep130);
|
|
824
|
+
} else {
|
|
825
|
+
// Print each algorithm table
|
|
826
|
+
for (const algo of squaringAlgos) {
|
|
827
|
+
console.log(`\n${sep130}`);
|
|
828
|
+
console.log(`=================== SQUARING SCALE: ${algo.title.toUpperCase()} ===================`);
|
|
829
|
+
console.log(`Expression: ${algo.expr} ${squaringInputText} ${"s ".repeat(squaringSteps).trim()}`);
|
|
830
|
+
console.log(`Initial: ${squaringInputText} (${countBitsValue(sInitialVal)} bits)`);
|
|
831
|
+
console.log(line130);
|
|
832
|
+
|
|
833
|
+
const headerCols = ["Step", "Bits", "Wire(B)", ...squaringLanes.map(l => `${l.name} (${l.memLabel})`)];
|
|
834
|
+
const colWidths = [4, 6, 7, ...squaringLanes.map(l => Math.max(22, l.name.length + l.memLabel.length + 5))];
|
|
835
|
+
console.log(headerCols.map((h, i) => (i < 3 ? h.padStart(colWidths[i]) : h.padEnd(colWidths[i]))).join(" | "));
|
|
836
|
+
console.log(colWidths.map(w => "-".repeat(w)).join("-|-"));
|
|
837
|
+
|
|
838
|
+
for (let step = 1; step <= squaringSteps; step++) {
|
|
839
|
+
const meta = stepMetadata[step - 1] || { bits: "?", wireLen: "?" };
|
|
840
|
+
const cols = [
|
|
841
|
+
String(step).padStart(colWidths[0]),
|
|
842
|
+
String(meta.bits ?? "?").padStart(colWidths[1]),
|
|
843
|
+
String(meta.wireLen ?? "?").padStart(colWidths[2])
|
|
844
|
+
];
|
|
845
|
+
for (let li = 0; li < squaringLanes.length; li++) {
|
|
846
|
+
const lane = squaringLanes[li];
|
|
847
|
+
const res = algo.results[lane.id][step - 1];
|
|
848
|
+
const w = colWidths[3 + li];
|
|
849
|
+
if (!res) {
|
|
850
|
+
cols.push("-".padEnd(w));
|
|
851
|
+
} else if (res.status === "ok") {
|
|
852
|
+
const t = res.timeMs < 10 ? `${res.timeMs.toFixed(2)}ms` : `${res.timeMs.toFixed(1)}ms`;
|
|
853
|
+
const m = res.memMb != null ? `${res.memMb}MB` : "";
|
|
854
|
+
cols.push(`${t} / ${m}`.padEnd(w));
|
|
855
|
+
} else {
|
|
856
|
+
let errText = res.error;
|
|
857
|
+
if (errText.includes("Maximum call stack")) {
|
|
858
|
+
errText = "Stack overflow (V8)";
|
|
859
|
+
} else if (errText.includes("status null") || errText.includes("failed with status")) {
|
|
860
|
+
errText = "Arena limit reached";
|
|
861
|
+
}
|
|
862
|
+
cols.push(errText.slice(0, w - 1).padEnd(w));
|
|
863
|
+
}
|
|
873
864
|
}
|
|
874
|
-
|
|
865
|
+
console.log(cols.join(" | "));
|
|
875
866
|
}
|
|
867
|
+
console.log(sep130);
|
|
876
868
|
}
|
|
877
|
-
console.log(cols.join(" | "));
|
|
878
869
|
}
|
|
879
|
-
console.log("==================================================================================================================================");
|
|
880
870
|
|
|
881
|
-
// Conformance check across all computed steps
|
|
871
|
+
// Conformance check across all computed steps and algorithms
|
|
882
872
|
let squaringConformanceOk = true;
|
|
883
873
|
for (let step = 1; step <= squaringSteps; step++) {
|
|
884
874
|
const meta = stepMetadata[step - 1];
|
|
885
875
|
if (!meta?.canonicalWire) continue;
|
|
886
|
-
for (const
|
|
887
|
-
const
|
|
888
|
-
|
|
889
|
-
if (res
|
|
890
|
-
if (
|
|
891
|
-
|
|
892
|
-
console.log(`Squaring conformance mismatch on step ${step} (${lane.name})`);
|
|
893
|
-
}
|
|
894
|
-
} else if (res.value) {
|
|
895
|
-
try {
|
|
896
|
-
const wireVal = decodeWire(meta.canonicalWire).value;
|
|
897
|
-
if (!safeDeepEqual(res.value, wireVal)) {
|
|
876
|
+
for (const algo of squaringAlgos) {
|
|
877
|
+
for (const lane of squaringLanes) {
|
|
878
|
+
const res = algo.results[lane.id][step - 1];
|
|
879
|
+
if (res?.status === "ok") {
|
|
880
|
+
if (res.wire) {
|
|
881
|
+
if (Buffer.compare(res.wire, meta.canonicalWire) !== 0) {
|
|
898
882
|
squaringConformanceOk = false;
|
|
899
|
-
console.log(`Squaring conformance mismatch on step ${step} (${lane.name})`);
|
|
883
|
+
console.log(`Squaring conformance mismatch on step ${step} (${algo.name} / ${lane.name})`);
|
|
900
884
|
}
|
|
901
|
-
}
|
|
885
|
+
} else if (res.value) {
|
|
886
|
+
try {
|
|
887
|
+
const wireVal = decodeWire(meta.canonicalWire).value;
|
|
888
|
+
if (!safeDeepEqual(res.value, wireVal)) {
|
|
889
|
+
squaringConformanceOk = false;
|
|
890
|
+
console.log(`Squaring conformance mismatch on step ${step} (${algo.name} / ${lane.name})`);
|
|
891
|
+
}
|
|
892
|
+
} catch {}
|
|
893
|
+
}
|
|
902
894
|
}
|
|
903
895
|
}
|
|
904
896
|
}
|
|
905
897
|
}
|
|
906
898
|
|
|
907
899
|
console.log("\nSquaring Performance & Scaling Summary:");
|
|
908
|
-
for (const
|
|
909
|
-
|
|
910
|
-
const
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
900
|
+
for (const algo of squaringAlgos) {
|
|
901
|
+
console.log(` Algorithm: ${algo.title}`);
|
|
902
|
+
for (const lane of squaringLanes) {
|
|
903
|
+
const okSteps = algo.results[lane.id].filter(r => r.status === "ok");
|
|
904
|
+
const maxStep = okSteps.length;
|
|
905
|
+
const lastOk = okSteps[okSteps.length - 1];
|
|
906
|
+
const peakMem = okSteps.reduce((max, r) => Math.max(max, r.memMb || 0), 0);
|
|
907
|
+
const failureReason = algo.results[lane.id][maxStep]?.error;
|
|
908
|
+
let statusDetail = "";
|
|
909
|
+
if (failureReason) {
|
|
910
|
+
if (failureReason.includes("Maximum call stack")) statusDetail = " (V8 call stack overflow)";
|
|
911
|
+
else if (failureReason.includes("status null") || failureReason.includes("failed with status")) statusDetail = " (arena capacity limit reached)";
|
|
912
|
+
else statusDetail = ` (${failureReason.slice(0, 30)})`;
|
|
913
|
+
}
|
|
914
|
+
const timeStr = lastOk ? ` (step ${maxStep} in ${lastOk.timeMs < 10 ? lastOk.timeMs.toFixed(2) : lastOk.timeMs.toFixed(1)}ms)` : "";
|
|
915
|
+
console.log(` - ${lane.name.padEnd(32)}: reached Step ${String(maxStep).padStart(2)}/${squaringSteps}${timeStr}, peak ${lane.memLabel}: ${peakMem}MB${statusDetail}`);
|
|
919
916
|
}
|
|
920
|
-
const timeStr = lastOk ? ` (step ${maxStep} in ${lastOk.timeMs < 10 ? lastOk.timeMs.toFixed(2) : lastOk.timeMs.toFixed(1)}ms)` : "";
|
|
921
|
-
console.log(` - ${lane.name.padEnd(32)}: reached Step ${String(maxStep).padStart(2)}/${squaringSteps}${timeStr}, peak ${lane.memLabel}: ${peakMem}MB${statusDetail}`);
|
|
922
917
|
}
|
|
923
918
|
|
|
924
919
|
if (squaringConformanceOk) {
|