@fraczak/k 7.0.1 → 7.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +78 -0
- package/DOCS/BIG_PICTURE.md +5 -5
- package/DOCS/FILE_FORMATS.md +241 -18
- package/DOCS/OBJECT_FILE_AND_PATTERN.md +9 -9
- package/DOCS/PATTERNS.md +3 -3
- package/DOCS/POLYMORPHIC_IR.md +104 -0
- 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/JOURNAL.md +288 -0
- package/README.md +13 -10
- package/access-table.mjs +343 -0
- package/backend-api.mjs +18 -0
- package/backends/llvm/README.md +20 -21
- package/backends/llvm/bin/k-llvm-build.mjs +105 -32
- package/backends/llvm/bin/k-llvm-compile.mjs +63 -24
- package/backends/llvm/bin/k-llvm.mjs +292 -0
- package/backends/llvm/runtime/krt.c +41 -0
- package/backends/llvm/runtime/krt.h +2 -0
- package/backends/llvm/src/cli.mjs +95 -17
- package/backends/llvm/src/executable.mjs +132 -15
- package/backends/llvm/src/kvm2llvm.mjs +50 -23
- package/backends/llvm/src/llvm.mjs +133 -20
- package/backends/llvm/tests/integration.sh +11 -11
- package/backends/llvm/{bin → tests}/k-llvm-run.mjs +48 -22
- package/backends/llvm/tests/perf-support.mjs +25 -58
- package/backends/llvm/tests/test-llvm.mjs +25 -4
- package/backends/wasm/bin/k-wasm-compile.mjs +46 -20
- package/backends/wasm/bin/k-wasm-run.mjs +61 -20
- package/backends/wasm/bin/k-wasm.mjs +47 -13
- package/backends/wasm/src/cli.mjs +141 -19
- package/backends/wasm/src/wasm.mjs +132 -24
- package/backends/wasm/tests/test-wasm.mjs +5 -5
- package/browser/repl-browser.mjs +16 -5
- package/browser/vfs-data.mjs +5 -5
- package/codecs/k-parse.mjs +29 -8
- package/codecs/k-print.mjs +22 -6
- package/codecs/runtime/codec-sdk.mjs +8 -4
- package/codecs/show.mjs +20 -6
- package/core.k +8 -8
- package/harness/format.mjs +2 -2
- package/harness/registry.mjs +0 -2
- package/k.mjs +56 -16
- package/kir.mjs +200 -75
- package/kvm.mjs +49 -12
- package/objects/compile.mjs +37 -23
- package/objects/decompile.mjs +40 -10
- package/objects/extract-aliases.mjs +44 -14
- package/objects/inspect.mjs +19 -14
- package/objects/validate.mjs +18 -11
- package/package.json +10 -7
- package/pkir.mjs +772 -0
- package/repl.mjs +36 -18
- package/scripts/benchmark-idempotency.mjs +12 -8
- package/scripts/benchmark.mjs +5 -9
- package/scripts/build-repl-html.mjs +16 -4
- package/scripts/perf-ieee.mjs +253 -103
- package/scripts/perf-int.mjs +585 -297
- package/scripts/perf-multiplication.mjs +66 -25
- package/scripts/perf-poly.mjs +292 -164
- package/scripts/run-tests.mjs +3 -0
- package/tests/code-derivation/pattern-envelope.mjs +4 -4
- package/tests/test-access-table.mjs +213 -0
- package/tests/test-kvm-polymorphic.mjs +2 -2
- package/tests/test-pkir.mjs +530 -0
- package/tests/test-polymorphic-tdd.mjs +196 -0
- package/tests/test-repl-html.mjs +15 -2
- package/tests/test-repl.mjs +30 -1
- 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/backends/llvm/bin/k-llvm-jit.mjs +0 -160
- 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,35 @@ 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, Wasm, and
|
|
61
|
+
across Native JS, kVM interpreter, LLVM, Wasm, and PKIR 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.
|
|
68
|
+
--pkir-only
|
|
69
|
+
Run only PKIR backend.
|
|
69
70
|
-h, --help
|
|
70
71
|
Show this help message and exit.
|
|
71
72
|
|
|
72
73
|
Environment Variables:
|
|
73
74
|
BACKENDS
|
|
74
|
-
Target backend to run: 'llvm', 'wasm', or '
|
|
75
|
+
Target backend to run: 'llvm', 'wasm', or 'pkir'.
|
|
75
76
|
LLVM_ONLY
|
|
76
77
|
Run only LLVM backend ('1' to enable).
|
|
77
78
|
WASM_ONLY
|
|
78
79
|
Run only WebAssembly backend ('1' to enable).
|
|
79
|
-
|
|
80
|
-
Run only
|
|
80
|
+
PKIR_ONLY
|
|
81
|
+
Run only PKIR backend ('1' to enable).
|
|
81
82
|
BACKENDS_ONLY
|
|
82
83
|
Skip JS and kVM interpreter baselines ('1' to enable).
|
|
84
|
+
SQUARING_LLVM_ONLY
|
|
85
|
+
Run squaring benchmark on LLVM only ('1' to enable).
|
|
83
86
|
INPUTS
|
|
84
87
|
Comma-separated integer strings to benchmark.
|
|
85
88
|
ITERATIONS
|
|
86
89
|
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
90
|
LLVM_WARMUP_ITERATIONS
|
|
96
91
|
Warmup iterations for LLVM backend (default: 1).
|
|
97
92
|
LLVM_SPAWN_PER_CALL
|
|
@@ -125,15 +120,57 @@ if (process.argv.includes("-h") || process.argv.includes("--help")) {
|
|
|
125
120
|
|
|
126
121
|
// Backend selection
|
|
127
122
|
const backendsEnv = process.env.BACKENDS?.toLowerCase();
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
const
|
|
136
|
-
|
|
123
|
+
const backendsList = backendsEnv
|
|
124
|
+
? backendsEnv.split(",").map(s => s.trim())
|
|
125
|
+
: null;
|
|
126
|
+
const llvmPkirOnly = process.env.LLVM_PKIR_ONLY === "1"
|
|
127
|
+
|| (backendsList && backendsList.length === 1 &&
|
|
128
|
+
backendsList[0] === "llvm-pkir")
|
|
129
|
+
|| process.argv.includes("--llvm-pkir-only");
|
|
130
|
+
const pkirOnly = !llvmPkirOnly && (
|
|
131
|
+
process.env.PKIR_ONLY === "1"
|
|
132
|
+
|| (backendsList && backendsList.length === 1 &&
|
|
133
|
+
backendsList[0] === "pkir")
|
|
134
|
+
|| process.argv.includes("--pkir-only")
|
|
135
|
+
);
|
|
136
|
+
const llvmOnly = !pkirOnly && !llvmPkirOnly && (
|
|
137
|
+
process.env.LLVM_ONLY === "1"
|
|
138
|
+
|| (backendsList && backendsList.length === 1 &&
|
|
139
|
+
backendsList[0] === "llvm")
|
|
140
|
+
|| process.argv.includes("--llvm-only")
|
|
141
|
+
);
|
|
142
|
+
const wasmOnly = !pkirOnly && !llvmPkirOnly && !llvmOnly && (
|
|
143
|
+
process.env.WASM_ONLY === "1"
|
|
144
|
+
|| (backendsList && backendsList.length === 1 &&
|
|
145
|
+
backendsList[0] === "wasm")
|
|
146
|
+
|| process.argv.includes("--wasm-only")
|
|
147
|
+
);
|
|
148
|
+
const runLLVM = backendsList
|
|
149
|
+
? backendsList.includes("llvm")
|
|
150
|
+
: !wasmOnly && !pkirOnly && !llvmPkirOnly;
|
|
151
|
+
const runLLVMPKIR = backendsList
|
|
152
|
+
? backendsList.includes("llvm-pkir")
|
|
153
|
+
: !wasmOnly && !pkirOnly && !llvmOnly;
|
|
154
|
+
const runWasm = backendsList
|
|
155
|
+
? backendsList.includes("wasm")
|
|
156
|
+
: !llvmOnly && !pkirOnly && !llvmPkirOnly;
|
|
157
|
+
const runPKIR = backendsList
|
|
158
|
+
? backendsList.includes("pkir")
|
|
159
|
+
: !llvmOnly && !wasmOnly && !llvmPkirOnly;
|
|
160
|
+
const runBaselines = !process.env.BACKENDS_ONLY
|
|
161
|
+
&& (backendsList == null || backendsList.includes("kvm") ||
|
|
162
|
+
backendsList.includes("js"))
|
|
163
|
+
&& !llvmOnly && !wasmOnly && !pkirOnly && !llvmPkirOnly;
|
|
164
|
+
const squaringLlvmOnly = llvmOnly || llvmPkirOnly
|
|
165
|
+
|| process.env.SQUARING_LLVM_ONLY === "1"
|
|
166
|
+
|| process.argv.includes("--squaring-llvm-only");
|
|
167
|
+
|
|
168
|
+
function llvmLaneNameStr() {
|
|
169
|
+
return llvmLaneName();
|
|
170
|
+
}
|
|
171
|
+
function llvmPkirLaneNameStr() {
|
|
172
|
+
return llvmLaneName().replace("LLVM", "LLVM-PKIR");
|
|
173
|
+
}
|
|
137
174
|
|
|
138
175
|
const ops = ["plus", "minus", "times"];
|
|
139
176
|
const benchNames = Object.fromEntries(ops.map(op => [op, `bench_${op}`]));
|
|
@@ -143,9 +180,6 @@ const inputTexts = csvEnv(
|
|
|
143
180
|
);
|
|
144
181
|
const iterations = parsePositiveIntEnv("ITERATIONS", 3);
|
|
145
182
|
|
|
146
|
-
// ARM64 options
|
|
147
|
-
const arm64WarmupIterations = parseNonNegativeIntEnv("ARM64_WARMUP_ITERATIONS", 1);
|
|
148
|
-
const arm64OptLevel = process.env.ARM64_OPT || "-O2";
|
|
149
183
|
if (!process.env.K_ARENA_CAPACITY_MB && !process.env.K_ARENA_MB) {
|
|
150
184
|
process.env.K_ARENA_CAPACITY_MB = "4096";
|
|
151
185
|
}
|
|
@@ -164,6 +198,9 @@ function wasmLaneName() {
|
|
|
164
198
|
const mode = wasmPipe ? "persistent" : "in-process";
|
|
165
199
|
return `WebAssembly (${mode})`;
|
|
166
200
|
}
|
|
201
|
+
function pkirLaneName() {
|
|
202
|
+
return "PKIR (Wasm persistent)";
|
|
203
|
+
}
|
|
167
204
|
|
|
168
205
|
console.log("==> Initializing state and loading @fraczak/k/Examples/arithmetics.k");
|
|
169
206
|
const state = createState();
|
|
@@ -175,7 +212,7 @@ for (const op of ops) {
|
|
|
175
212
|
}
|
|
176
213
|
|
|
177
214
|
function intValue(text) {
|
|
178
|
-
return
|
|
215
|
+
return parseIntValue(text);
|
|
179
216
|
}
|
|
180
217
|
|
|
181
218
|
console.log("==> Preparing relations");
|
|
@@ -227,13 +264,37 @@ if (runLLVM) {
|
|
|
227
264
|
relHash: relation.relHash,
|
|
228
265
|
inputPattern: tc.inputPattern,
|
|
229
266
|
cacheDir,
|
|
230
|
-
sourceLabel:
|
|
267
|
+
sourceLabel:
|
|
268
|
+
`@fraczak/k/Examples/arithmetics.k#${relation.relationName}`,
|
|
269
|
+
usePKIR: false
|
|
231
270
|
});
|
|
232
271
|
}
|
|
233
272
|
codes.load(state.codes);
|
|
234
273
|
printCompileFailures(testSuite);
|
|
235
274
|
}
|
|
236
275
|
|
|
276
|
+
// Setup LLVM-PKIR
|
|
277
|
+
if (runLLVMPKIR) {
|
|
278
|
+
console.log("==> Compiling LLVM-PKIR executables");
|
|
279
|
+
for (const tc of testSuite) {
|
|
280
|
+
const relation = relations[tc.op];
|
|
281
|
+
tc.llvmPKIR = tryCompileCase({
|
|
282
|
+
object: relation.object,
|
|
283
|
+
relationName: relation.relationName,
|
|
284
|
+
relHash: relation.relHash,
|
|
285
|
+
inputPattern: tc.inputPattern,
|
|
286
|
+
cacheDir,
|
|
287
|
+
sourceLabel:
|
|
288
|
+
`@fraczak/k/Examples/arithmetics.k#${relation.relationName}`,
|
|
289
|
+
usePKIR: true
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
codes.load(state.codes);
|
|
293
|
+
printCompileFailures(
|
|
294
|
+
testSuite.map(tc => ({ ...tc, llvm: tc.llvmPKIR }))
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
|
|
237
298
|
// Setup Wasm
|
|
238
299
|
let wasmExports = null;
|
|
239
300
|
const wasmExes = new Map();
|
|
@@ -287,6 +348,35 @@ if (runWasm) {
|
|
|
287
348
|
}
|
|
288
349
|
}
|
|
289
350
|
|
|
351
|
+
// Setup PKIR (persistent runner)
|
|
352
|
+
const pkirExes = new Map();
|
|
353
|
+
if (runPKIR) {
|
|
354
|
+
console.log("==> Compiling PKIR executables (persistent runner)...");
|
|
355
|
+
for (const op of ops) {
|
|
356
|
+
const relation = relations[op];
|
|
357
|
+
try {
|
|
358
|
+
const wasmPath = path.join(cacheDir, `perf_int_pkir_${op}.wasm`);
|
|
359
|
+
const exePath = path.join(cacheDir, `perf_int_pkir_${op}.exe`);
|
|
360
|
+
const wasmBuf = await compileWasmArtifactFromObject(relation.object, {
|
|
361
|
+
entry: "__main__",
|
|
362
|
+
usePKIR: true
|
|
363
|
+
});
|
|
364
|
+
fs.writeFileSync(wasmPath, wasmBuf);
|
|
365
|
+
const runBin = fileURLToPath(import.meta.resolve("../backends/wasm/bin/k-wasm-run.mjs"));
|
|
366
|
+
fs.writeFileSync(exePath, `#!/bin/sh\nexec node "${runBin}" "${wasmPath}" "$@"\n`, { mode: 0o755 });
|
|
367
|
+
pkirExes.set(op, { status: "ok", exePath, wasmPath });
|
|
368
|
+
} catch (error) {
|
|
369
|
+
pkirExes.set(op, {
|
|
370
|
+
status: "failed",
|
|
371
|
+
error: error.stack || error.message || String(error)
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
for (const tc of testSuite) {
|
|
376
|
+
tc.pkir = pkirExes.get(tc.op);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
290
380
|
function createWasmRunner(testSuite) {
|
|
291
381
|
const wasmCases = testSuite.filter(tc => tc.wasm?.status === "ok");
|
|
292
382
|
const servers = new Map();
|
|
@@ -315,37 +405,32 @@ function createWasmRunner(testSuite) {
|
|
|
315
405
|
};
|
|
316
406
|
}
|
|
317
407
|
|
|
318
|
-
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
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
|
-
});
|
|
408
|
+
function createPKIRRunner(testSuite) {
|
|
409
|
+
const pkirCases = testSuite.filter(tc => tc.pkir?.status === "ok");
|
|
410
|
+
const servers = new Map();
|
|
411
|
+
function serverFor(exePath) {
|
|
412
|
+
let server = servers.get(exePath);
|
|
413
|
+
if (server == null) {
|
|
414
|
+
server = new PersistentExecutable(exePath);
|
|
415
|
+
servers.set(exePath, server);
|
|
337
416
|
}
|
|
417
|
+
return server;
|
|
338
418
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
419
|
+
|
|
420
|
+
return {
|
|
421
|
+
async run(iterations) {
|
|
422
|
+
if (pkirCases.length === 0) return null;
|
|
423
|
+
return runTimedIterationsAsync(iterations, async () => {
|
|
424
|
+
for (const tc of pkirCases) {
|
|
425
|
+
await serverFor(tc.pkir.exePath).request(tc.inputWire);
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
},
|
|
429
|
+
serverFor,
|
|
430
|
+
close() {
|
|
431
|
+
for (const server of servers.values()) server.close();
|
|
347
432
|
}
|
|
348
|
-
}
|
|
433
|
+
};
|
|
349
434
|
}
|
|
350
435
|
|
|
351
436
|
function printBenchmarkDescription() {
|
|
@@ -355,9 +440,10 @@ function printBenchmarkDescription() {
|
|
|
355
440
|
lanes.push("Native JS (Envelope-Free)");
|
|
356
441
|
lanes.push("kVM Interpreter (Env-Free)");
|
|
357
442
|
}
|
|
358
|
-
if (runLLVM) lanes.push(
|
|
443
|
+
if (runLLVM) lanes.push(llvmLaneNameStr());
|
|
444
|
+
if (runLLVMPKIR) lanes.push(llvmPkirLaneNameStr());
|
|
359
445
|
if (runWasm) lanes.push(wasmLaneName());
|
|
360
|
-
if (
|
|
446
|
+
if (runPKIR) lanes.push(pkirLaneName());
|
|
361
447
|
|
|
362
448
|
console.log("==> Benchmark description");
|
|
363
449
|
console.log(" source: @fraczak/k/Examples/arithmetics.k");
|
|
@@ -369,16 +455,17 @@ function printBenchmarkDescription() {
|
|
|
369
455
|
console.log(` llvm-ready cases: ${testSuite.filter(tc => tc.llvm?.status === "ok").length}`);
|
|
370
456
|
console.log(` llvm warmup iterations: ${llvmWarmupIterations}`);
|
|
371
457
|
}
|
|
458
|
+
if (runLLVMPKIR) {
|
|
459
|
+
console.log(` llvm-pkir ready cases: ${testSuite.filter(tc => tc.llvmPKIR?.status === "ok").length}`);
|
|
460
|
+
console.log(` llvm-pkir warmup iterations: ${llvmWarmupIterations}`);
|
|
461
|
+
}
|
|
372
462
|
if (runWasm) {
|
|
373
463
|
console.log(` wasm runner mode: ${wasmPipe ? "persistent pipe" : "in-process"}`);
|
|
374
464
|
console.log(` wasm warmup iterations: ${wasmWarmupIterations}`);
|
|
375
465
|
if (!wasmPipe) console.log(` wasm arena reset: ${wasmReset ? "yes" : "no"}`);
|
|
376
466
|
}
|
|
377
|
-
if (
|
|
378
|
-
console.log(`
|
|
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"}`);
|
|
467
|
+
if (runPKIR) {
|
|
468
|
+
console.log(` pkir warmup iterations: ${wasmWarmupIterations}`);
|
|
382
469
|
}
|
|
383
470
|
console.log(` iterations: ${iterations}`);
|
|
384
471
|
console.log(` benchmark lanes: ${lanes.join("; ")}`);
|
|
@@ -414,6 +501,22 @@ if (runLLVM) {
|
|
|
414
501
|
}
|
|
415
502
|
}
|
|
416
503
|
|
|
504
|
+
// 2b. LLVM-PKIR lane
|
|
505
|
+
let llvmPkirResult = null;
|
|
506
|
+
if (runLLVMPKIR) {
|
|
507
|
+
const llvmPkirCases = testSuite.map(tc => ({ ...tc, llvm: tc.llvmPKIR }));
|
|
508
|
+
const llvmPkirRunner = createLLVMRunner(llvmPkirCases);
|
|
509
|
+
try {
|
|
510
|
+
if (llvmWarmupIterations > 0) {
|
|
511
|
+
console.log(`==> Warming LLVM-PKIR executables (${llvmWarmupIterations} iterations)...`);
|
|
512
|
+
await llvmPkirRunner.run(llvmWarmupIterations);
|
|
513
|
+
}
|
|
514
|
+
llvmPkirResult = await llvmPkirRunner.run(iterations);
|
|
515
|
+
} finally {
|
|
516
|
+
llvmPkirRunner.close();
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
417
520
|
// 3. Wasm lane
|
|
418
521
|
let wasmResult = null;
|
|
419
522
|
if (runWasm) {
|
|
@@ -458,22 +561,21 @@ if (runWasm) {
|
|
|
458
561
|
}
|
|
459
562
|
}
|
|
460
563
|
|
|
461
|
-
// 4.
|
|
462
|
-
let
|
|
463
|
-
if (
|
|
464
|
-
const
|
|
465
|
-
if (
|
|
466
|
-
const
|
|
564
|
+
// 4. PKIR lane
|
|
565
|
+
let pkirResult = null;
|
|
566
|
+
if (runPKIR) {
|
|
567
|
+
const pkirCases = testSuite.filter(tc => tc.pkir?.status === "ok");
|
|
568
|
+
if (pkirCases.length === testSuite.length) {
|
|
569
|
+
const pkirRunner = createPKIRRunner(testSuite);
|
|
467
570
|
try {
|
|
468
|
-
if (
|
|
469
|
-
console.log(`==> Warming
|
|
470
|
-
await
|
|
571
|
+
if (wasmWarmupIterations > 0) {
|
|
572
|
+
console.log(`==> Warming PKIR (${wasmWarmupIterations} iterations)...`);
|
|
573
|
+
await pkirRunner.run(wasmWarmupIterations);
|
|
471
574
|
}
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
arm64Result = await arm64Runner.run(iterations);
|
|
575
|
+
console.log(`==> Running PKIR (${iterations} iterations)...`);
|
|
576
|
+
pkirResult = await pkirRunner.run(iterations);
|
|
475
577
|
} finally {
|
|
476
|
-
|
|
578
|
+
pkirRunner.close();
|
|
477
579
|
}
|
|
478
580
|
}
|
|
479
581
|
}
|
|
@@ -489,14 +591,18 @@ if (runBaselines) {
|
|
|
489
591
|
console.log(`${laneIndex++}. kVM Interpreter (Env-Free): ${formatTiming(kvmFreeResult)}`);
|
|
490
592
|
}
|
|
491
593
|
if (runLLVM) {
|
|
492
|
-
|
|
594
|
+
const name = llvmLaneNameStr().padEnd(29);
|
|
595
|
+
console.log(`${laneIndex++}. ${name} ${formatTiming(llvmResult)}`);
|
|
596
|
+
}
|
|
597
|
+
if (runLLVMPKIR) {
|
|
598
|
+
const name = llvmPkirLaneNameStr().padEnd(29);
|
|
599
|
+
console.log(`${laneIndex++}. ${name} ${formatTiming(llvmPkirResult)}`);
|
|
493
600
|
}
|
|
494
601
|
if (runWasm) {
|
|
495
602
|
console.log(`${laneIndex++}. ${wasmLaneName().padEnd(29)} ${formatTiming(wasmResult)}`);
|
|
496
603
|
}
|
|
497
|
-
if (
|
|
498
|
-
|
|
499
|
-
console.log(`${laneIndex++}. ${arm64LaneName(arm64OptLevel).padEnd(29)} ${timingStr}`);
|
|
604
|
+
if (runPKIR) {
|
|
605
|
+
console.log(`${laneIndex++}. ${pkirLaneName().padEnd(29)} ${formatTiming(pkirResult)}`);
|
|
500
606
|
}
|
|
501
607
|
console.log("=================================================================\n");
|
|
502
608
|
|
|
@@ -525,6 +631,24 @@ for (const tc of testSuite) {
|
|
|
525
631
|
}
|
|
526
632
|
}
|
|
527
633
|
|
|
634
|
+
if (runLLVMPKIR) {
|
|
635
|
+
if (tc.llvmPKIR.status !== "ok") {
|
|
636
|
+
tc.llvmPkirConformance = "compile-failed";
|
|
637
|
+
} else {
|
|
638
|
+
try {
|
|
639
|
+
const outputWire = await runExecutable(tc.llvmPKIR.exePath, tc.inputWire);
|
|
640
|
+
const actual = decodeWire(outputWire).value;
|
|
641
|
+
assert.deepEqual(toPlainObject(actual), toPlainObject(tc.expected));
|
|
642
|
+
tc.llvmPkirConformance = "ok";
|
|
643
|
+
} catch (error) {
|
|
644
|
+
tc.llvmPkirConformance = "failed";
|
|
645
|
+
tc.llvmPKIR.error = error.stack || error.message || String(error);
|
|
646
|
+
console.log(`LLVM-PKIR conformance failure for ${tc.op}(${tc.label}):`);
|
|
647
|
+
console.log(tc.llvmPKIR.error.split("\n").slice(0, 8).join("\n"));
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
528
652
|
if (runWasm) {
|
|
529
653
|
if (wasmPipe) {
|
|
530
654
|
if (tc.wasm?.status !== "ok") {
|
|
@@ -563,23 +687,24 @@ for (const tc of testSuite) {
|
|
|
563
687
|
}
|
|
564
688
|
}
|
|
565
689
|
|
|
566
|
-
if (
|
|
567
|
-
if (tc.
|
|
568
|
-
tc.
|
|
690
|
+
if (runPKIR) {
|
|
691
|
+
if (tc.pkir?.status !== "ok") {
|
|
692
|
+
tc.pkirConformance = "compile-failed";
|
|
569
693
|
} else {
|
|
570
694
|
try {
|
|
571
|
-
const outputWire = await runExecutable(tc.
|
|
695
|
+
const outputWire = await runExecutable(tc.pkir.exePath, tc.inputWire);
|
|
572
696
|
const actual = decodeWire(outputWire).value;
|
|
573
697
|
assert.deepEqual(toPlainObject(actual), toPlainObject(tc.expected));
|
|
574
|
-
tc.
|
|
698
|
+
tc.pkirConformance = "ok";
|
|
575
699
|
} catch (error) {
|
|
576
|
-
tc.
|
|
577
|
-
tc.
|
|
578
|
-
console.log(`
|
|
579
|
-
console.log(tc.
|
|
700
|
+
tc.pkirConformance = "failed";
|
|
701
|
+
tc.pkir.error = error.stack || error.message || String(error);
|
|
702
|
+
console.log(`PKIR conformance failure for ${tc.op}(${tc.label}):`);
|
|
703
|
+
console.log(tc.pkir.error.split("\n").slice(0, 8).join("\n"));
|
|
580
704
|
}
|
|
581
705
|
}
|
|
582
706
|
}
|
|
707
|
+
|
|
583
708
|
}
|
|
584
709
|
|
|
585
710
|
const validationSummary = [];
|
|
@@ -588,24 +713,29 @@ if (runLLVM) {
|
|
|
588
713
|
const llvmOk = testSuite.filter(tc => tc.llvmConformance === "ok").length;
|
|
589
714
|
validationSummary.push(`LLVM ${llvmOk}/${testSuite.length}`);
|
|
590
715
|
}
|
|
716
|
+
if (runLLVMPKIR) {
|
|
717
|
+
const pkirOk = testSuite.filter(tc => tc.llvmPkirConformance === "ok").length;
|
|
718
|
+
validationSummary.push(`LLVM-PKIR ${pkirOk}/${testSuite.length}`);
|
|
719
|
+
}
|
|
591
720
|
if (runWasm) {
|
|
592
721
|
const wasmOk = testSuite.filter(tc => tc.wasmConformance === "ok").length;
|
|
593
722
|
validationSummary.push(`Wasm ${wasmOk}/${testSuite.length}`);
|
|
594
723
|
}
|
|
595
|
-
if (
|
|
596
|
-
const
|
|
597
|
-
validationSummary.push(`
|
|
724
|
+
if (runPKIR) {
|
|
725
|
+
const pkirOk = testSuite.filter(tc => tc.pkirConformance === "ok").length;
|
|
726
|
+
validationSummary.push(`PKIR ${pkirOk}/${testSuite.length}`);
|
|
598
727
|
}
|
|
599
728
|
|
|
600
729
|
console.log(`Conformance validation: ${validationSummary.join(", ")} cases match expected values.`);
|
|
601
730
|
if (runLLVM && shouldStrictFail(testSuite)) process.exitCode = 1;
|
|
731
|
+
if (runLLVMPKIR && shouldStrictFail(testSuite.map(tc => ({ ...tc, llvm: tc.llvmPKIR })))) process.exitCode = 1;
|
|
602
732
|
if (runWasm && testSuite.some(tc => tc.wasmConformance !== "ok")) process.exitCode = 1;
|
|
603
|
-
if (
|
|
733
|
+
if (runPKIR && testSuite.some(tc => tc.pkirConformance !== "ok")) process.exitCode = 1;
|
|
604
734
|
|
|
605
|
-
//
|
|
735
|
+
// =======================================================================
|
|
606
736
|
// Squaring Scale & Memory Profiling Benchmark
|
|
607
|
-
//
|
|
608
|
-
//
|
|
737
|
+
// Expressions: s = {()x,()y}times; and s = {()x,()y}karatsuba;
|
|
738
|
+
// =======================================================================
|
|
609
739
|
|
|
610
740
|
function countBitsValue(val) {
|
|
611
741
|
let count = 0;
|
|
@@ -644,189 +774,274 @@ const squaringInputText = process.env.SQUARING_INPUT || "987654321";
|
|
|
644
774
|
const squaringStepTimeoutMs = parsePositiveIntEnv("SQUARING_STEP_TIMEOUT_MS", 60000);
|
|
645
775
|
|
|
646
776
|
console.log("\n=================== SQUARING SCALE & MEMORY BENCHMARK ===================");
|
|
647
|
-
console.log(`
|
|
648
|
-
console.log(`Evaluation: ${squaringSteps} successive squarings doubling bit length each step`);
|
|
777
|
+
console.log(`Algorithms: times vs karatsuba (${squaringSteps} successive squarings doubling bit length)`);
|
|
649
778
|
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
const
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
});
|
|
779
|
+
// Load and initialize Karatsuba multiplication relation
|
|
780
|
+
const karatsubaPath = fileURLToPath(import.meta.resolve("@fraczak/k/Examples/karatsuba-mult.k"));
|
|
781
|
+
const karatsubaSource = fs.readFileSync(karatsubaPath, "utf8");
|
|
782
|
+
const karatsubaLibrarySource = karatsubaSource.replace(/\s*\(\)\s*$/, "\n");
|
|
783
|
+
const stateKara = createState();
|
|
784
|
+
await evaluateInput(`:load ${karatsubaPath}`, stateKara);
|
|
785
|
+
codes.load(stateKara.codes);
|
|
656
786
|
|
|
657
787
|
const sInitialVal = intValue(squaringInputText);
|
|
658
788
|
const { inputWire: sInitWire, inputPattern: sInputPattern } = wireInput(sInitialVal);
|
|
659
789
|
|
|
790
|
+
const squaringAlgos = [
|
|
791
|
+
{
|
|
792
|
+
id: "times",
|
|
793
|
+
name: "times",
|
|
794
|
+
title: "Long Multiplication (times)",
|
|
795
|
+
expr: "s = {()x,()y}times;",
|
|
796
|
+
librarySource: arithmeticsLibrarySource,
|
|
797
|
+
sourceLabel: "@fraczak/k/Examples/arithmetics.k#s",
|
|
798
|
+
state: state,
|
|
799
|
+
relName: "s"
|
|
800
|
+
},
|
|
801
|
+
{
|
|
802
|
+
id: "karatsuba",
|
|
803
|
+
name: "karatsuba",
|
|
804
|
+
title: "Karatsuba Multiplication (karatsuba)",
|
|
805
|
+
expr: "s = {()x,()y}karatsuba;",
|
|
806
|
+
librarySource: karatsubaLibrarySource,
|
|
807
|
+
sourceLabel: "@fraczak/k/Examples/karatsuba-mult.k#s",
|
|
808
|
+
state: stateKara,
|
|
809
|
+
relName: "s"
|
|
810
|
+
}
|
|
811
|
+
];
|
|
812
|
+
|
|
660
813
|
// Build executables for persistent servers
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
inputPattern: sInputPattern,
|
|
668
|
-
cacheDir,
|
|
669
|
-
sourceLabel: "@fraczak/k/Examples/arithmetics.k#s"
|
|
814
|
+
for (const algo of squaringAlgos) {
|
|
815
|
+
await evaluateInput(algo.expr, algo.state);
|
|
816
|
+
codes.load(algo.state.codes);
|
|
817
|
+
algo.rel = prepareRelation(algo.state, algo.relName, {
|
|
818
|
+
source: `${algo.librarySource}\n${algo.expr}\n${algo.relName}`,
|
|
819
|
+
sourceLabel: algo.sourceLabel
|
|
670
820
|
});
|
|
671
|
-
if (sLlvmCompiled.status === "ok") {
|
|
672
|
-
sLlvmServer = new PersistentExecutable(sLlvmCompiled.exePath);
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
821
|
|
|
676
|
-
|
|
677
|
-
if (
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
822
|
+
algo.llvmServer = null;
|
|
823
|
+
if (runLLVM) {
|
|
824
|
+
const sLlvmCompiled = tryCompileCase({
|
|
825
|
+
object: algo.rel.object,
|
|
826
|
+
relationName: algo.rel.relationName,
|
|
827
|
+
relHash: algo.rel.relHash,
|
|
828
|
+
inputPattern: sInputPattern,
|
|
829
|
+
cacheDir,
|
|
830
|
+
sourceLabel: algo.sourceLabel,
|
|
831
|
+
usePKIR: false
|
|
832
|
+
});
|
|
833
|
+
if (sLlvmCompiled.status === "ok") {
|
|
834
|
+
algo.llvmServer = new PersistentExecutable(sLlvmCompiled.exePath);
|
|
835
|
+
}
|
|
688
836
|
}
|
|
689
|
-
}
|
|
690
837
|
|
|
691
|
-
|
|
692
|
-
if (
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
838
|
+
algo.llvmPkirServer = null;
|
|
839
|
+
if (runLLVMPKIR) {
|
|
840
|
+
const sLlvmPkirCompiled = tryCompileCase({
|
|
841
|
+
object: algo.rel.object,
|
|
842
|
+
relationName: algo.rel.relationName,
|
|
843
|
+
relHash: algo.rel.relHash,
|
|
844
|
+
inputPattern: sInputPattern,
|
|
845
|
+
cacheDir,
|
|
846
|
+
sourceLabel: algo.sourceLabel,
|
|
847
|
+
usePKIR: true
|
|
699
848
|
});
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
849
|
+
if (sLlvmPkirCompiled.status === "ok") {
|
|
850
|
+
algo.llvmPkirServer =
|
|
851
|
+
new PersistentExecutable(sLlvmPkirCompiled.exePath);
|
|
852
|
+
}
|
|
703
853
|
}
|
|
854
|
+
|
|
855
|
+
algo.wasmServer = null;
|
|
856
|
+
if (runWasm && !squaringLlvmOnly) {
|
|
857
|
+
try {
|
|
858
|
+
const sWasmPath = path.join(cacheDir, `perf_int_wasm_${algo.id}.wasm`);
|
|
859
|
+
const sWasmExe = path.join(cacheDir, `perf_int_wasm_${algo.id}.exe`);
|
|
860
|
+
const wasmBuf = await compileWasmArtifactFromObject(algo.rel.object, { entry: "__main__" });
|
|
861
|
+
fs.writeFileSync(sWasmPath, wasmBuf);
|
|
862
|
+
const runBin = fileURLToPath(import.meta.resolve("../backends/wasm/bin/k-wasm-run.mjs"));
|
|
863
|
+
fs.writeFileSync(sWasmExe, `#!/bin/sh\nexec node "${runBin}" "${sWasmPath}" "$@"\n`, { mode: 0o755 });
|
|
864
|
+
algo.wasmServer = new PersistentExecutable(sWasmExe);
|
|
865
|
+
} catch (e) {
|
|
866
|
+
console.log(`Failed to build Wasm executable for '${algo.id}':`, e.message);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
algo.pkirServer = null;
|
|
871
|
+
if (runPKIR && !squaringLlvmOnly) {
|
|
872
|
+
try {
|
|
873
|
+
const sPkirPath = path.join(cacheDir, `perf_int_pkir_${algo.id}.wasm`);
|
|
874
|
+
const sPkirExe = path.join(cacheDir, `perf_int_pkir_${algo.id}.exe`);
|
|
875
|
+
const wasmBuf = await compileWasmArtifactFromObject(algo.rel.object, {
|
|
876
|
+
entry: "__main__",
|
|
877
|
+
usePKIR: true
|
|
878
|
+
});
|
|
879
|
+
fs.writeFileSync(sPkirPath, wasmBuf);
|
|
880
|
+
const runBin = fileURLToPath(import.meta.resolve("../backends/wasm/bin/k-wasm-run.mjs"));
|
|
881
|
+
fs.writeFileSync(sPkirExe, `#!/bin/sh\nexec node "${runBin}" "${sPkirPath}" "$@"\n`, { mode: 0o755 });
|
|
882
|
+
algo.pkirServer = new PersistentExecutable(sPkirExe);
|
|
883
|
+
} catch (e) {
|
|
884
|
+
console.log(`Failed to build PKIR executable for '${algo.id}':`, e.message);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
algo.results = {};
|
|
704
889
|
}
|
|
705
890
|
|
|
706
891
|
const squaringLanes = [];
|
|
707
|
-
if (runBaselines) {
|
|
892
|
+
if (runBaselines && !squaringLlvmOnly) {
|
|
708
893
|
squaringLanes.push({ id: "jsAware", name: "Native JS (Aware)", type: "jsAware", memLabel: "heap" });
|
|
709
894
|
squaringLanes.push({ id: "jsFree", name: "Native JS (Free)", type: "jsFree", memLabel: "heap" });
|
|
710
895
|
squaringLanes.push({ id: "kvm", name: "kVM (Env-Free)", type: "kvm", memLabel: "heap" });
|
|
711
896
|
}
|
|
712
|
-
if (runLLVM
|
|
713
|
-
squaringLanes.push({
|
|
897
|
+
if (runLLVM) {
|
|
898
|
+
squaringLanes.push({
|
|
899
|
+
id: "llvm",
|
|
900
|
+
name: llvmLaneNameStr(),
|
|
901
|
+
type: "persistent",
|
|
902
|
+
memLabel: "peak RSS"
|
|
903
|
+
});
|
|
714
904
|
}
|
|
715
|
-
if (
|
|
716
|
-
squaringLanes.push({
|
|
905
|
+
if (runLLVMPKIR) {
|
|
906
|
+
squaringLanes.push({
|
|
907
|
+
id: "llvm-pkir",
|
|
908
|
+
name: llvmPkirLaneNameStr(),
|
|
909
|
+
type: "persistent",
|
|
910
|
+
memLabel: "peak RSS"
|
|
911
|
+
});
|
|
717
912
|
}
|
|
718
|
-
if (
|
|
719
|
-
squaringLanes.push({ id: "
|
|
913
|
+
if (runWasm && !squaringLlvmOnly) {
|
|
914
|
+
squaringLanes.push({ id: "wasm", name: wasmLaneName(), type: "persistent", memLabel: "peak RSS" });
|
|
915
|
+
}
|
|
916
|
+
if (runPKIR && !squaringLlvmOnly) {
|
|
917
|
+
squaringLanes.push({ id: "pkir", name: pkirLaneName(), type: "persistent", memLabel: "peak RSS" });
|
|
720
918
|
}
|
|
721
919
|
|
|
722
|
-
const
|
|
723
|
-
for (const lane of squaringLanes)
|
|
920
|
+
for (const algo of squaringAlgos) {
|
|
921
|
+
for (const lane of squaringLanes) {
|
|
922
|
+
algo.results[lane.id] = [];
|
|
923
|
+
}
|
|
924
|
+
}
|
|
724
925
|
const stepMetadata = [];
|
|
725
926
|
|
|
726
927
|
try {
|
|
727
|
-
// 1. Persistent child processes (
|
|
928
|
+
// 1. Persistent child processes (Wasm, PKIR, LLVM)
|
|
728
929
|
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
|
-
|
|
930
|
+
for (const algo of squaringAlgos) {
|
|
931
|
+
const server = lane.id === "llvm"
|
|
932
|
+
? algo.llvmServer
|
|
933
|
+
: lane.id === "llvm-pkir"
|
|
934
|
+
? algo.llvmPkirServer
|
|
935
|
+
: lane.id === "pkir"
|
|
936
|
+
? algo.pkirServer
|
|
937
|
+
: algo.wasmServer;
|
|
938
|
+
if (!server) continue;
|
|
939
|
+
console.log(`==> Running ${lane.name} squaring [${algo.name}]...`);
|
|
940
|
+
let curWire = sInitWire;
|
|
941
|
+
for (let step = 1; step <= squaringSteps; step++) {
|
|
942
|
+
try {
|
|
943
|
+
const t0 = performance.now();
|
|
944
|
+
curWire = await server.request(curWire);
|
|
945
|
+
const t1 = performance.now();
|
|
946
|
+
const timeMs = t1 - t0;
|
|
947
|
+
const mem = getProcessMemory(server.child.pid);
|
|
948
|
+
algo.results[lane.id].push({ status: "ok", timeMs, memMb: mem.peakRssMb, wire: curWire });
|
|
949
|
+
if (!stepMetadata[step - 1]) {
|
|
950
|
+
stepMetadata[step - 1] = {
|
|
951
|
+
wireLen: curWire.length,
|
|
952
|
+
bits: null,
|
|
953
|
+
canonicalWire: curWire
|
|
954
|
+
};
|
|
955
|
+
}
|
|
956
|
+
if (timeMs > squaringStepTimeoutMs) break;
|
|
957
|
+
} catch (e) {
|
|
958
|
+
algo.results[lane.id].push({ status: "failed", error: e.message?.split("\n")[0] || String(e) });
|
|
959
|
+
break;
|
|
745
960
|
}
|
|
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
961
|
}
|
|
751
962
|
}
|
|
752
963
|
}
|
|
753
964
|
|
|
754
965
|
// 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
|
|
966
|
+
if (runBaselines && !squaringLlvmOnly) {
|
|
967
|
+
for (const algo of squaringAlgos) {
|
|
968
|
+
// JS Aware
|
|
969
|
+
console.log(`==> Running Native JS (Aware) squaring [${algo.name}]...`);
|
|
970
|
+
let curValAware = sInitialVal;
|
|
971
|
+
run.defs = algo.state;
|
|
972
|
+
for (let step = 1; step <= squaringSteps; step++) {
|
|
973
|
+
try {
|
|
974
|
+
const t0 = performance.now();
|
|
975
|
+
curValAware = run(codes.find, algo.rel.relDef.def, curValAware, algo.rel.relDef.typePatternGraph);
|
|
976
|
+
const t1 = performance.now();
|
|
977
|
+
const timeMs = t1 - t0;
|
|
978
|
+
const mem = getNodeMemory();
|
|
979
|
+
algo.results.jsAware.push({ status: "ok", timeMs, memMb: mem.heapMb, value: curValAware });
|
|
980
|
+
if (stepMetadata[step - 1] && stepMetadata[step - 1].bits == null) {
|
|
981
|
+
stepMetadata[step - 1].bits = countBitsValue(curValAware);
|
|
982
|
+
}
|
|
983
|
+
if (timeMs > squaringStepTimeoutMs) break;
|
|
984
|
+
} catch (e) {
|
|
985
|
+
algo.results.jsAware.push({ status: "failed", error: e.message?.split("\n")[0] || String(e) });
|
|
986
|
+
break;
|
|
770
987
|
}
|
|
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
988
|
}
|
|
776
|
-
}
|
|
777
989
|
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
990
|
+
// JS Free
|
|
991
|
+
console.log(`==> Running Native JS (Free) squaring [${algo.name}]...`);
|
|
992
|
+
let curValFree = sInitialVal;
|
|
993
|
+
run_converged.defs = algo.state;
|
|
994
|
+
for (let step = 1; step <= squaringSteps; step++) {
|
|
995
|
+
try {
|
|
996
|
+
const t0 = performance.now();
|
|
997
|
+
curValFree = run_converged(codes.find, algo.rel.relDef.def, curValFree, algo.rel.relDef.typePatternGraph);
|
|
998
|
+
const t1 = performance.now();
|
|
999
|
+
const timeMs = t1 - t0;
|
|
1000
|
+
const mem = getNodeMemory();
|
|
1001
|
+
algo.results.jsFree.push({ status: "ok", timeMs, memMb: mem.heapMb, value: curValFree });
|
|
1002
|
+
if (stepMetadata[step - 1] && stepMetadata[step - 1].bits == null) {
|
|
1003
|
+
stepMetadata[step - 1].bits = countBitsValue(curValFree);
|
|
1004
|
+
}
|
|
1005
|
+
if (timeMs > squaringStepTimeoutMs) break;
|
|
1006
|
+
} catch (e) {
|
|
1007
|
+
algo.results.jsFree.push({ status: "failed", error: e.message?.split("\n")[0] || String(e) });
|
|
1008
|
+
break;
|
|
792
1009
|
}
|
|
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
1010
|
}
|
|
798
|
-
}
|
|
799
1011
|
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
1012
|
+
// kVM
|
|
1013
|
+
console.log(`==> Running kVM Interpreter squaring [${algo.name}]...`);
|
|
1014
|
+
let curValKvm = sInitialVal;
|
|
1015
|
+
const kvmContext = { rels: algo.state.rels, findCode: codes.find, options: { envelopeFree: true } };
|
|
1016
|
+
for (let step = 1; step <= squaringSteps; step++) {
|
|
1017
|
+
try {
|
|
1018
|
+
const t0 = performance.now();
|
|
1019
|
+
curValKvm = executeKVM(algo.rel.kvmFunc, curValKvm, kvmContext);
|
|
1020
|
+
const t1 = performance.now();
|
|
1021
|
+
const timeMs = t1 - t0;
|
|
1022
|
+
const mem = getNodeMemory();
|
|
1023
|
+
algo.results.kvm.push({ status: "ok", timeMs, memMb: mem.heapMb, value: curValKvm });
|
|
1024
|
+
if (stepMetadata[step - 1] && stepMetadata[step - 1].bits == null) {
|
|
1025
|
+
stepMetadata[step - 1].bits = countBitsValue(curValKvm);
|
|
1026
|
+
}
|
|
1027
|
+
if (timeMs > squaringStepTimeoutMs) break;
|
|
1028
|
+
} catch (e) {
|
|
1029
|
+
algo.results.kvm.push({ status: "failed", error: e.message?.split("\n")[0] || String(e) });
|
|
1030
|
+
break;
|
|
814
1031
|
}
|
|
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
1032
|
}
|
|
820
1033
|
}
|
|
821
1034
|
}
|
|
822
|
-
|
|
823
1035
|
} finally {
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
1036
|
+
for (const algo of squaringAlgos) {
|
|
1037
|
+
if (algo.wasmServer) algo.wasmServer.close();
|
|
1038
|
+
if (algo.pkirServer) algo.pkirServer.close();
|
|
1039
|
+
if (algo.llvmServer) algo.llvmServer.close();
|
|
1040
|
+
if (algo.llvmPkirServer) algo.llvmPkirServer.close();
|
|
1041
|
+
}
|
|
827
1042
|
}
|
|
828
1043
|
|
|
829
|
-
// For any remaining steps without bit counts, decode canonicalWire safely
|
|
1044
|
+
// For any remaining steps without bit counts, decode canonicalWire safely
|
|
830
1045
|
for (let step = 1; step <= squaringSteps; step++) {
|
|
831
1046
|
const meta = stepMetadata[step - 1];
|
|
832
1047
|
if (meta && meta.bits == null && meta.canonicalWire) {
|
|
@@ -838,87 +1053,160 @@ for (let step = 1; step <= squaringSteps; step++) {
|
|
|
838
1053
|
}
|
|
839
1054
|
|
|
840
1055
|
// Display results table
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
const
|
|
846
|
-
const
|
|
847
|
-
|
|
848
|
-
console.log(
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
1056
|
+
const sep130 = "=".repeat(130);
|
|
1057
|
+
const line130 = "-".repeat(130);
|
|
1058
|
+
|
|
1059
|
+
if (squaringLanes.length === 1 && (squaringLanes[0].id === "llvm" || squaringLanes[0].id === "llvm-pkir")) {
|
|
1060
|
+
const laneId = squaringLanes[0].id;
|
|
1061
|
+
const laneName = squaringLanes[0].name;
|
|
1062
|
+
// LLVM Only side-by-side comparison table
|
|
1063
|
+
console.log(`\n${sep130}`);
|
|
1064
|
+
console.log(`=================== SQUARING SCALE & ALGORITHM BENCHMARK (${laneName}) ===================`);
|
|
1065
|
+
console.log(`Algorithms: times vs karatsuba on ${laneName}`);
|
|
1066
|
+
console.log(`Initial: ${squaringInputText} (${countBitsValue(sInitialVal)} bits)`);
|
|
1067
|
+
console.log(line130);
|
|
1068
|
+
|
|
1069
|
+
const tAlgo = squaringAlgos[0];
|
|
1070
|
+
const kAlgo = squaringAlgos[1];
|
|
1071
|
+
const colW = [4, 6, 7, 34, 34, 15];
|
|
1072
|
+
const hCols = [
|
|
1073
|
+
"Step",
|
|
1074
|
+
"Bits",
|
|
1075
|
+
"Wire(B)",
|
|
1076
|
+
`${laneName} times (peak RSS)`,
|
|
1077
|
+
`${laneName} karatsuba (peak RSS)`,
|
|
1078
|
+
"Speedup (T/K)"
|
|
856
1079
|
];
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
}
|
|
872
|
-
|
|
1080
|
+
console.log(hCols.map((h, i) => (i < 3 ? h.padStart(colW[i]) : h.padEnd(colW[i]))).join(" | "));
|
|
1081
|
+
console.log(colW.map(w => "-".repeat(w)).join("-|-"));
|
|
1082
|
+
|
|
1083
|
+
for (let step = 1; step <= squaringSteps; step++) {
|
|
1084
|
+
const meta = stepMetadata[step - 1] || { bits: "?", wireLen: "?" };
|
|
1085
|
+
const tRes = tAlgo.results[laneId][step - 1];
|
|
1086
|
+
const kRes = kAlgo.results[laneId][step - 1];
|
|
1087
|
+
|
|
1088
|
+
const formatCell = (res, w) => {
|
|
1089
|
+
if (!res) return "-".padEnd(w);
|
|
1090
|
+
if (res.status === "ok") {
|
|
1091
|
+
const t = res.timeMs < 10 ? `${res.timeMs.toFixed(2)}ms` : `${res.timeMs.toFixed(1)}ms`;
|
|
1092
|
+
const m = res.memMb != null ? `${res.memMb}MB` : "";
|
|
1093
|
+
return `${t} / ${m}`.padEnd(w);
|
|
1094
|
+
}
|
|
1095
|
+
let errText = res.error || "error";
|
|
1096
|
+
if (errText.includes("Maximum call stack")) errText = "Stack overflow (V8)";
|
|
1097
|
+
else if (errText.includes("status null") || errText.includes("failed with status")) errText = "Arena limit reached";
|
|
1098
|
+
return errText.slice(0, w - 1).padEnd(w);
|
|
1099
|
+
};
|
|
1100
|
+
|
|
1101
|
+
let speedupStr = "-";
|
|
1102
|
+
if (tRes?.status === "ok" && kRes?.status === "ok" && kRes.timeMs > 0) {
|
|
1103
|
+
speedupStr = `${(tRes.timeMs / kRes.timeMs).toFixed(2)}x`;
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
const row = [
|
|
1107
|
+
String(step).padStart(colW[0]),
|
|
1108
|
+
String(meta.bits ?? "?").padStart(colW[1]),
|
|
1109
|
+
String(meta.wireLen ?? "?").padStart(colW[2]),
|
|
1110
|
+
formatCell(tRes, colW[3]),
|
|
1111
|
+
formatCell(kRes, colW[4]),
|
|
1112
|
+
speedupStr.padEnd(colW[5])
|
|
1113
|
+
];
|
|
1114
|
+
console.log(row.join(" | "));
|
|
1115
|
+
}
|
|
1116
|
+
console.log(sep130);
|
|
1117
|
+
} else {
|
|
1118
|
+
// Print each algorithm table
|
|
1119
|
+
for (const algo of squaringAlgos) {
|
|
1120
|
+
console.log(`\n${sep130}`);
|
|
1121
|
+
console.log(`=================== SQUARING SCALE: ${algo.title.toUpperCase()} ===================`);
|
|
1122
|
+
console.log(`Expression: ${algo.expr} ${squaringInputText} ${"s ".repeat(squaringSteps).trim()}`);
|
|
1123
|
+
console.log(`Initial: ${squaringInputText} (${countBitsValue(sInitialVal)} bits)`);
|
|
1124
|
+
console.log(line130);
|
|
1125
|
+
|
|
1126
|
+
const headerCols = ["Step", "Bits", "Wire(B)", ...squaringLanes.map(l => `${l.name} (${l.memLabel})`)];
|
|
1127
|
+
const colWidths = [4, 6, 7, ...squaringLanes.map(l => Math.max(22, l.name.length + l.memLabel.length + 5))];
|
|
1128
|
+
console.log(headerCols.map((h, i) => (i < 3 ? h.padStart(colWidths[i]) : h.padEnd(colWidths[i]))).join(" | "));
|
|
1129
|
+
console.log(colWidths.map(w => "-".repeat(w)).join("-|-"));
|
|
1130
|
+
|
|
1131
|
+
for (let step = 1; step <= squaringSteps; step++) {
|
|
1132
|
+
const meta = stepMetadata[step - 1] || { bits: "?", wireLen: "?" };
|
|
1133
|
+
const cols = [
|
|
1134
|
+
String(step).padStart(colWidths[0]),
|
|
1135
|
+
String(meta.bits ?? "?").padStart(colWidths[1]),
|
|
1136
|
+
String(meta.wireLen ?? "?").padStart(colWidths[2])
|
|
1137
|
+
];
|
|
1138
|
+
for (let li = 0; li < squaringLanes.length; li++) {
|
|
1139
|
+
const lane = squaringLanes[li];
|
|
1140
|
+
const res = algo.results[lane.id][step - 1];
|
|
1141
|
+
const w = colWidths[3 + li];
|
|
1142
|
+
if (!res) {
|
|
1143
|
+
cols.push("-".padEnd(w));
|
|
1144
|
+
} else if (res.status === "ok") {
|
|
1145
|
+
const t = res.timeMs < 10 ? `${res.timeMs.toFixed(2)}ms` : `${res.timeMs.toFixed(1)}ms`;
|
|
1146
|
+
const m = res.memMb != null ? `${res.memMb}MB` : "";
|
|
1147
|
+
cols.push(`${t} / ${m}`.padEnd(w));
|
|
1148
|
+
} else {
|
|
1149
|
+
let errText = res.error;
|
|
1150
|
+
if (errText.includes("Maximum call stack")) {
|
|
1151
|
+
errText = "Stack overflow (V8)";
|
|
1152
|
+
} else if (errText.includes("status null") || errText.includes("failed with status")) {
|
|
1153
|
+
errText = "Arena limit reached";
|
|
1154
|
+
}
|
|
1155
|
+
cols.push(errText.slice(0, w - 1).padEnd(w));
|
|
1156
|
+
}
|
|
873
1157
|
}
|
|
874
|
-
|
|
1158
|
+
console.log(cols.join(" | "));
|
|
875
1159
|
}
|
|
1160
|
+
console.log(sep130);
|
|
876
1161
|
}
|
|
877
|
-
console.log(cols.join(" | "));
|
|
878
1162
|
}
|
|
879
|
-
console.log("==================================================================================================================================");
|
|
880
1163
|
|
|
881
|
-
// Conformance check across all computed steps
|
|
1164
|
+
// Conformance check across all computed steps and algorithms
|
|
882
1165
|
let squaringConformanceOk = true;
|
|
883
1166
|
for (let step = 1; step <= squaringSteps; step++) {
|
|
884
1167
|
const meta = stepMetadata[step - 1];
|
|
885
1168
|
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)) {
|
|
1169
|
+
for (const algo of squaringAlgos) {
|
|
1170
|
+
for (const lane of squaringLanes) {
|
|
1171
|
+
const res = algo.results[lane.id][step - 1];
|
|
1172
|
+
if (res?.status === "ok") {
|
|
1173
|
+
if (res.wire) {
|
|
1174
|
+
if (Buffer.compare(res.wire, meta.canonicalWire) !== 0) {
|
|
898
1175
|
squaringConformanceOk = false;
|
|
899
|
-
console.log(`Squaring conformance mismatch on step ${step} (${lane.name})`);
|
|
1176
|
+
console.log(`Squaring conformance mismatch on step ${step} (${algo.name} / ${lane.name})`);
|
|
900
1177
|
}
|
|
901
|
-
}
|
|
1178
|
+
} else if (res.value) {
|
|
1179
|
+
try {
|
|
1180
|
+
const wireVal = decodeWire(meta.canonicalWire).value;
|
|
1181
|
+
if (!safeDeepEqual(res.value, wireVal)) {
|
|
1182
|
+
squaringConformanceOk = false;
|
|
1183
|
+
console.log(`Squaring conformance mismatch on step ${step} (${algo.name} / ${lane.name})`);
|
|
1184
|
+
}
|
|
1185
|
+
} catch {}
|
|
1186
|
+
}
|
|
902
1187
|
}
|
|
903
1188
|
}
|
|
904
1189
|
}
|
|
905
1190
|
}
|
|
906
1191
|
|
|
907
1192
|
console.log("\nSquaring Performance & Scaling Summary:");
|
|
908
|
-
for (const
|
|
909
|
-
|
|
910
|
-
const
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
1193
|
+
for (const algo of squaringAlgos) {
|
|
1194
|
+
console.log(` Algorithm: ${algo.title}`);
|
|
1195
|
+
for (const lane of squaringLanes) {
|
|
1196
|
+
const okSteps = algo.results[lane.id].filter(r => r.status === "ok");
|
|
1197
|
+
const maxStep = okSteps.length;
|
|
1198
|
+
const lastOk = okSteps[okSteps.length - 1];
|
|
1199
|
+
const peakMem = okSteps.reduce((max, r) => Math.max(max, r.memMb || 0), 0);
|
|
1200
|
+
const failureReason = algo.results[lane.id][maxStep]?.error;
|
|
1201
|
+
let statusDetail = "";
|
|
1202
|
+
if (failureReason) {
|
|
1203
|
+
if (failureReason.includes("Maximum call stack")) statusDetail = " (V8 call stack overflow)";
|
|
1204
|
+
else if (failureReason.includes("status null") || failureReason.includes("failed with status")) statusDetail = " (arena capacity limit reached)";
|
|
1205
|
+
else statusDetail = ` (${failureReason.slice(0, 30)})`;
|
|
1206
|
+
}
|
|
1207
|
+
const timeStr = lastOk ? ` (step ${maxStep} in ${lastOk.timeMs < 10 ? lastOk.timeMs.toFixed(2) : lastOk.timeMs.toFixed(1)}ms)` : "";
|
|
1208
|
+
console.log(` - ${lane.name.padEnd(32)}: reached Step ${String(maxStep).padStart(2)}/${squaringSteps}${timeStr}, peak ${lane.memLabel}: ${peakMem}MB${statusDetail}`);
|
|
1209
|
+
}
|
|
922
1210
|
}
|
|
923
1211
|
|
|
924
1212
|
if (squaringConformanceOk) {
|