@fraczak/k 6.3.3 → 6.5.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 +17 -0
- package/DOCS/CODECS.md +258 -0
- package/DOCS/DICTIONARY.md +1172 -0
- package/DOCS/KIR_SPECIALIZATION_DESIGN.md +51 -46
- package/DOCS/KIR_V1.md +221 -0
- package/DOCS/KVM_EXECUTION_MODEL.md +56 -19
- package/DOCS/OBJECT_FILE_AND_PATTERN.md +2 -1
- package/DOCS/PROFILING_AND_TRACING.md +196 -0
- package/DOCS/REPL.md +61 -4
- package/DOCS/WEB_REPL_REQUIREMENTS.md +135 -0
- package/DOCS/dependency-graph.md +144 -0
- package/Examples/arithmetics.k +108 -82
- package/Examples/bnat.k +1 -1
- package/Examples/buda.k +44 -0
- package/Examples/ieee.k +300 -8671
- package/Examples/poly.k +67 -0
- package/README.md +68 -19
- package/Value.mjs +8 -2
- package/augmentation.mjs +9 -7
- package/backend-api.mjs +11 -2
- package/backends/arm64/README.md +104 -0
- package/backends/arm64/bin/k-arm64-compile.mjs +101 -0
- package/backends/arm64/bin/k-arm64-run.mjs +58 -0
- package/backends/arm64/bin/k-arm64.mjs +88 -0
- package/backends/arm64/scripts/run-tests.mjs +41 -0
- package/backends/arm64/src/arm64.mjs +229 -0
- package/backends/arm64/src/cli.mjs +155 -0
- package/backends/arm64/src/kvm2arm64.mjs +906 -0
- package/backends/arm64/src/runtime.c +728 -0
- package/backends/arm64/tests/integration.sh +112 -0
- package/backends/arm64/tests/test-arm64.mjs +240 -0
- package/backends/llvm/README.md +135 -0
- package/backends/llvm/bin/k-llvm-build.mjs +71 -0
- package/backends/llvm/bin/k-llvm-compile.mjs +68 -0
- package/backends/llvm/bin/k-llvm-jit.mjs +160 -0
- package/backends/llvm/bin/k-llvm-run.mjs +90 -0
- package/backends/llvm/runtime/krt.c +1508 -0
- package/backends/llvm/runtime/krt.h +109 -0
- package/backends/llvm/scripts/conformance.mjs +76 -0
- package/backends/llvm/scripts/run-tests.mjs +28 -0
- package/backends/llvm/src/executable.mjs +562 -0
- package/backends/llvm/src/kvm2llvm.mjs +998 -0
- package/backends/llvm/src/llvm.mjs +1092 -0
- package/backends/llvm/tests/composition-driver.c +22 -0
- package/backends/llvm/tests/identity-driver.c +27 -0
- package/backends/llvm/tests/integration.sh +153 -0
- package/backends/llvm/tests/perf-support.mjs +735 -0
- package/backends/llvm/tests/product-driver.c +23 -0
- package/backends/llvm/tests/projection-driver.c +19 -0
- package/backends/llvm/tests/relation-driver.c +29 -0
- package/backends/llvm/tests/test-llvm.mjs +240 -0
- package/backends/llvm/tests/union-driver.c +31 -0
- package/backends/llvm/tests/variant-driver.c +19 -0
- package/backends/llvm/tests/variant-projection-driver.c +21 -0
- package/backends/wasm/DOCS/USE-WEB-ASSEMBLY.md +233 -0
- package/backends/wasm/README.md +97 -0
- package/backends/wasm/TECHNICAL-DEBT.md +58 -0
- package/backends/wasm/bin/k-wasm-compile.mjs +50 -0
- package/backends/wasm/bin/k-wasm-run.mjs +140 -0
- package/backends/wasm/bin/k-wasm.mjs +49 -0
- package/backends/wasm/demo_ieee.mjs +319 -0
- package/backends/wasm/runtime.wat +475 -0
- package/backends/wasm/scripts/run-tests.mjs +41 -0
- package/backends/wasm/src/cli.mjs +133 -0
- package/backends/wasm/src/kvm2wasm.mjs +697 -0
- package/backends/wasm/src/wasm.mjs +1054 -0
- package/backends/wasm/tests/integration.sh +182 -0
- package/backends/wasm/tests/perf-support.mjs +336 -0
- package/backends/wasm/tests/test-wasm.mjs +625 -0
- package/browser/repl-browser.mjs +1584 -0
- package/browser/shims/assert.mjs +19 -0
- package/browser/shims/buffer.mjs +224 -0
- package/browser/shims/crypto.mjs +103 -0
- package/browser/shims/fs.mjs +159 -0
- package/browser/shims/os.mjs +17 -0
- package/browser/shims/path.mjs +54 -0
- package/browser/shims/process.mjs +44 -0
- package/browser/shims/readline.mjs +11 -0
- package/browser/shims/url.mjs +12 -0
- package/browser/vfs-data.mjs +19 -0
- package/codecs/README.md +3 -0
- package/codecs/runtime/show-value.mjs +12 -3
- package/conformance/helper-reuse/case.json +8 -0
- package/conformance/helper-reuse/expected.kv +1 -0
- package/conformance/helper-reuse/input.kv +1 -0
- package/conformance/helper-reuse/program.k +2 -0
- package/conformance/identity/case.json +8 -0
- package/conformance/identity/expected.kv +1 -0
- package/conformance/identity/input.kv +1 -0
- package/conformance/identity/program.k +1 -0
- package/conformance/product-projection/case.json +8 -0
- package/conformance/product-projection/expected.kv +1 -0
- package/conformance/product-projection/input.kv +1 -0
- package/conformance/product-projection/program.k +1 -0
- package/conformance/run.mjs +149 -0
- package/conformance/variant-construction/case.json +8 -0
- package/conformance/variant-construction/expected.kv +1 -0
- package/conformance/variant-construction/input.kv +1 -0
- package/conformance/variant-construction/program.k +1 -0
- package/harness/adapters/arm64.mjs +105 -0
- package/harness/adapters/kvm.mjs +144 -0
- package/harness/adapters/llvm.mjs +118 -0
- package/harness/adapters/native.mjs +77 -0
- package/harness/adapters/wasm.mjs +169 -0
- package/harness/backend.mjs +95 -0
- package/harness/cli.mjs +130 -0
- package/harness/format.mjs +95 -0
- package/harness/index.mjs +25 -0
- package/harness/persistent.mjs +121 -0
- package/harness/registry.mjs +44 -0
- package/harness/suite.mjs +257 -0
- package/harness/suites/poly.mjs +156 -0
- package/harness/trace.mjs +50 -0
- package/harness/util.mjs +92 -0
- package/intrinsics.mjs +41 -0
- package/kir.mjs +399 -0
- package/kvm.mjs +585 -81
- package/objects/README.md +37 -3
- package/objects/compile.mjs +15 -22
- package/objects/inspect.mjs +96 -0
- package/objects/validate.mjs +415 -0
- package/package.json +51 -19
- package/repl-codecs.mjs +105 -6
- package/repl.mjs +335 -49
- package/run.mjs +2 -7
- package/scripts/benchmark.mjs +406 -22
- package/scripts/build-repl-html.mjs +1784 -0
- package/scripts/perf-ieee.mjs +586 -0
- package/scripts/perf-int.mjs +531 -0
- package/scripts/perf-poly.mjs +945 -0
- package/scripts/render-benchmark.mjs +12 -8
- package/scripts/run-tests.mjs +6 -0
- package/tests/integration.sh +19 -2
- package/tests/test-arithmetics.mjs +54 -0
- package/tests/test-ieee-arithmetic.mjs +4 -0
- package/tests/test-kir.mjs +85 -0
- package/tests/test-kvm-polymorphic.mjs +278 -0
- package/tests/test-kvm.mjs +25 -6
- package/tests/test-poly.mjs +129 -0
- package/tests/test-repl-html.mjs +303 -0
- package/tests/test-repl.mjs +144 -1
- package/tests/test-validate-object.mjs +40 -0
- package/a.klib +0 -1
- package/a.kvm +0 -142676
- package/a.wasm +0 -0
- package/benchmark-results.html +0 -532
- package/benchmark-results.json +0 -480
- package/k-vscode/.vscode/launch.json +0 -14
- package/k-vscode/README.md +0 -75
- package/k-vscode/extension.js +0 -273
- package/k-vscode/language-configuration.json +0 -42
- package/k-vscode/package.json +0 -79
- package/k-vscode/snippets/k.json +0 -92
- package/k-vscode/syntaxes/k.tmLanguage.json +0 -599
- package/k-vscode/test/background-ranges.test.js +0 -50
- package/k-vscode/themes/k-contrast-color-theme.json +0 -104
- package/scripts/git-hooks/pre-commit +0 -10
package/scripts/benchmark.mjs
CHANGED
|
@@ -11,9 +11,16 @@ import { performance } from "node:perf_hooks";
|
|
|
11
11
|
import {
|
|
12
12
|
codes,
|
|
13
13
|
createState,
|
|
14
|
+
decodeWire,
|
|
15
|
+
encodeToWire,
|
|
14
16
|
evaluateInput,
|
|
15
17
|
executeKVM,
|
|
18
|
+
exportPatternGraph,
|
|
16
19
|
lowerToKVM,
|
|
20
|
+
objectToKVMArtifact,
|
|
21
|
+
retypeObjectRelationForBackend,
|
|
22
|
+
patternToPropertyList,
|
|
23
|
+
propertyListToPattern,
|
|
17
24
|
parseFloat64,
|
|
18
25
|
run,
|
|
19
26
|
run_converged,
|
|
@@ -21,9 +28,24 @@ import {
|
|
|
21
28
|
Value
|
|
22
29
|
} from "../backend-api.mjs";
|
|
23
30
|
import { parse as parseIntValue } from "../codecs/int.mjs";
|
|
31
|
+
import {
|
|
32
|
+
compileObjectBuffer as compileBackendObjectBuffer,
|
|
33
|
+
decodeObject as decodeBackendObject
|
|
34
|
+
} from "../object.mjs";
|
|
35
|
+
import {
|
|
36
|
+
compileWasmArtifactFromKVM,
|
|
37
|
+
metadataFromModule,
|
|
38
|
+
readArenaValue as readWasmArenaValue,
|
|
39
|
+
wasmPtr,
|
|
40
|
+
writeValueToArena as writeWasmValueToArena
|
|
41
|
+
} from "../backends/wasm/src/wasm.mjs";
|
|
42
|
+
import { emitLLVMModule } from "../backends/llvm/src/llvm.mjs";
|
|
43
|
+
import { compileLLVMToExecutable, stdioDriverSource } from "../backends/llvm/src/executable.mjs";
|
|
24
44
|
|
|
25
45
|
const scriptPath = fileURLToPath(import.meta.url);
|
|
26
46
|
const root = path.resolve(path.dirname(scriptPath), "..");
|
|
47
|
+
const kRoot = root;
|
|
48
|
+
const llvmRoot = path.join(root, "backends/llvm");
|
|
27
49
|
const resultsPath = path.join(root, "benchmark-results.json");
|
|
28
50
|
|
|
29
51
|
const bigA = "1234567890".repeat(22);
|
|
@@ -47,9 +69,14 @@ const cases = [
|
|
|
47
69
|
const modes = [
|
|
48
70
|
{ name: "Native JS (Envelope-Aware)", kind: "native-aware" },
|
|
49
71
|
{ name: "Native JS (Envelope-Free)", kind: "native-free" },
|
|
50
|
-
{ name: "kVM Interpreter (Env-Free)", kind: "kvm-free" }
|
|
72
|
+
{ name: "kVM Interpreter (Env-Free)", kind: "kvm-free" },
|
|
73
|
+
{ name: "KIR-P Export", kind: "kir-p" },
|
|
74
|
+
{ name: "k-wasm", kind: "wasm" },
|
|
75
|
+
{ name: "k-llvm-jit", kind: "llvm-jit" }
|
|
51
76
|
];
|
|
52
77
|
|
|
78
|
+
let cachedLLVMBackendFingerprint = null;
|
|
79
|
+
|
|
53
80
|
function csvEnv(name, fallback) {
|
|
54
81
|
return (process.env[name] || fallback)
|
|
55
82
|
.split(",")
|
|
@@ -93,6 +120,15 @@ function digestValue(value) {
|
|
|
93
120
|
};
|
|
94
121
|
}
|
|
95
122
|
|
|
123
|
+
function digestResult(value, relDef) {
|
|
124
|
+
const outputPattern = relationPattern(relDef, 1);
|
|
125
|
+
const wire = encodeToWire(value, outputPattern);
|
|
126
|
+
return {
|
|
127
|
+
digest: crypto.createHash("sha256").update(wire).digest("hex").slice(0, 16),
|
|
128
|
+
bytes: wire.length
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
96
132
|
function formatMs(value) {
|
|
97
133
|
return value == null ? "-" : value.toFixed(2);
|
|
98
134
|
}
|
|
@@ -106,10 +142,320 @@ function pad(value, width) {
|
|
|
106
142
|
return text.length >= width ? text : text + " ".repeat(width - text.length);
|
|
107
143
|
}
|
|
108
144
|
|
|
145
|
+
function sha256() {
|
|
146
|
+
return crypto.createHash("sha256");
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function suiteSourcePath(suite) {
|
|
150
|
+
return path.join(kRoot, suite === "ieee" ? "Examples/ieee.k" : "Examples/arithmetics.k");
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function sourceNameForSuite(suite) {
|
|
154
|
+
return suite === "ieee" ? "Examples/ieee.k" : "Examples/arithmetics.k";
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function stripTrailingUnitMain(source) {
|
|
158
|
+
return source.replace(/\s*\(\)\s*$/, "\n");
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function sourceForCase(testCase) {
|
|
162
|
+
const sourcePath = suiteSourcePath(testCase.suite);
|
|
163
|
+
const source = stripTrailingUnitMain(fs.readFileSync(sourcePath, "utf8"));
|
|
164
|
+
return `${source}\n${testCase.rel}\n`;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function relationPattern(relDef, index) {
|
|
168
|
+
const graph = relDef.typePatternGraph;
|
|
169
|
+
const patternId = graph.find(relDef.def.patterns[index]);
|
|
170
|
+
return patternToPropertyList(exportPatternGraph(graph, patternId));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function canonicalPattern(propertyList) {
|
|
174
|
+
return patternToPropertyList(propertyListToPattern(propertyList));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function wireInput(inputVal, relDef) {
|
|
178
|
+
const inputPattern = relationPattern(relDef, 0);
|
|
179
|
+
const inputWire = encodeToWire(inputVal, inputPattern);
|
|
180
|
+
return { inputPattern, inputWire };
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function backendObjectForCase(testCase) {
|
|
184
|
+
const source = sourceForCase(testCase);
|
|
185
|
+
return decodeBackendObject(compileBackendObjectBuffer(source, {
|
|
186
|
+
source: `${sourceNameForSuite(testCase.suite)}#${testCase.rel}`
|
|
187
|
+
}));
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function backendKIRP(testCase, inputPattern, object = null) {
|
|
191
|
+
const backendObject = object || backendObjectForCase(testCase);
|
|
192
|
+
return retypeObjectRelationForBackend(backendObject, backendObject.main, inputPattern, {
|
|
193
|
+
source: `${sourceNameForSuite(testCase.suite)}#${testCase.rel}`
|
|
194
|
+
}).kir;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function backendKVMArtifact(testCase, inputPattern, object = null) {
|
|
198
|
+
const backendObject = object || backendObjectForCase(testCase);
|
|
199
|
+
return objectToKVMArtifact(backendObject, backendObject.main, inputPattern, {
|
|
200
|
+
source: `${sourceNameForSuite(testCase.suite)}#${testCase.rel}`
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function createTagRegistry(entries) {
|
|
205
|
+
const tagToId = new Map();
|
|
206
|
+
const idToTag = new Map();
|
|
207
|
+
let nextId = 1;
|
|
208
|
+
|
|
209
|
+
for (const entry of entries) {
|
|
210
|
+
if (!entry || typeof entry.tag !== "string" || !Number.isInteger(entry.id) || entry.id < 1) {
|
|
211
|
+
throw new Error("WebAssembly artifact metadata contains an invalid tag entry");
|
|
212
|
+
}
|
|
213
|
+
if (tagToId.has(entry.tag) || idToTag.has(entry.id)) {
|
|
214
|
+
throw new Error("WebAssembly artifact metadata contains a duplicate tag entry");
|
|
215
|
+
}
|
|
216
|
+
tagToId.set(entry.tag, entry.id);
|
|
217
|
+
idToTag.set(entry.id, entry.tag);
|
|
218
|
+
nextId = Math.max(nextId, entry.id + 1);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
getId(tag) {
|
|
223
|
+
if (!tagToId.has(tag)) {
|
|
224
|
+
tagToId.set(tag, nextId);
|
|
225
|
+
idToTag.set(nextId, tag);
|
|
226
|
+
nextId++;
|
|
227
|
+
}
|
|
228
|
+
return tagToId.get(tag);
|
|
229
|
+
},
|
|
230
|
+
getTag(id) {
|
|
231
|
+
return idToTag.get(id) ?? null;
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
async function prepareWasmRunner(testCase, relation, inputVal, state) {
|
|
237
|
+
const relDef = relation.relDef;
|
|
238
|
+
const { inputPattern, inputWire } = wireInput(inputVal, relDef);
|
|
239
|
+
const kvmArtifact = backendKVMArtifact(testCase, inputPattern);
|
|
240
|
+
const artifact = await compileWasmArtifactFromKVM(kvmArtifact);
|
|
241
|
+
const module = await WebAssembly.compile(artifact);
|
|
242
|
+
const metadata = metadataFromModule(module);
|
|
243
|
+
const instance = await WebAssembly.instantiate(module);
|
|
244
|
+
const wasmExports = instance.exports;
|
|
245
|
+
const tags = createTagRegistry(metadata.tags);
|
|
246
|
+
const inputPatternGraph = propertyListToPattern(metadata.inputPattern);
|
|
247
|
+
const outputPatternGraph = propertyListToPattern(metadata.outputPattern);
|
|
248
|
+
const { value } = decodeWire(inputWire);
|
|
249
|
+
const arenaInputValues = new Map();
|
|
250
|
+
const inputPtr = writeWasmValueToArena(
|
|
251
|
+
wasmExports,
|
|
252
|
+
value,
|
|
253
|
+
inputPatternGraph,
|
|
254
|
+
0,
|
|
255
|
+
arenaInputValues,
|
|
256
|
+
tags,
|
|
257
|
+
metadata.inputPattern
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
return {
|
|
261
|
+
run() {
|
|
262
|
+
const mark = typeof wasmExports.arena_mark === "function" ? wasmExports.arena_mark() : null;
|
|
263
|
+
const result = wasmExports[metadata.entry](inputPtr);
|
|
264
|
+
if (result[1] !== 1) {
|
|
265
|
+
throw new Error("Wasm relation execution failed (returned false)");
|
|
266
|
+
}
|
|
267
|
+
const output = readWasmArenaValue(
|
|
268
|
+
wasmExports,
|
|
269
|
+
wasmPtr(result[0]),
|
|
270
|
+
outputPatternGraph,
|
|
271
|
+
0,
|
|
272
|
+
metadata.outputPattern,
|
|
273
|
+
new Map(arenaInputValues),
|
|
274
|
+
tags
|
|
275
|
+
);
|
|
276
|
+
if (mark != null && typeof wasmExports.arena_reset === "function") {
|
|
277
|
+
wasmExports.arena_reset(mark);
|
|
278
|
+
}
|
|
279
|
+
return output;
|
|
280
|
+
},
|
|
281
|
+
cleanup() {}
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function llvmBackendFingerprint() {
|
|
286
|
+
if (cachedLLVMBackendFingerprint != null) return cachedLLVMBackendFingerprint;
|
|
287
|
+
const hash = sha256();
|
|
288
|
+
for (const relPath of ["src/llvm.mjs", "src/executable.mjs", "runtime/krt.c", "runtime/krt.h"]) {
|
|
289
|
+
hash.update(relPath);
|
|
290
|
+
hash.update("\0");
|
|
291
|
+
hash.update(fs.readFileSync(path.join(llvmRoot, relPath)));
|
|
292
|
+
hash.update("\0");
|
|
293
|
+
}
|
|
294
|
+
cachedLLVMBackendFingerprint = hash.digest("hex").slice(0, 16);
|
|
295
|
+
return cachedLLVMBackendFingerprint;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function llvmRuntimeMode(suite) {
|
|
299
|
+
if (suite === "ieee") {
|
|
300
|
+
return process.env.K_LLVM_IEEE_RUNTIME_MODE || process.env.K_LLVM_RUNTIME_MODE || "compact";
|
|
301
|
+
}
|
|
302
|
+
return process.env.K_LLVM_RUNTIME_MODE || "fast";
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function llvmClangOpt(suite) {
|
|
306
|
+
if (suite === "ieee") {
|
|
307
|
+
return process.env.K_LLVM_IEEE_CLANG_OPT || process.env.K_LLVM_CLANG_OPT || "-O0";
|
|
308
|
+
}
|
|
309
|
+
return process.env.K_LLVM_CLANG_OPT || "-O3";
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function llvmCacheDir() {
|
|
313
|
+
const dir = process.env.K_LLVM_CACHE_DIR || path.join(os.tmpdir(), "k-parent-benchmark-llvm-cache");
|
|
314
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
315
|
+
return dir;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function compileLLVMExecutable({ kvmArtifact, testCase }) {
|
|
319
|
+
const runtimeMode = llvmRuntimeMode(testCase.suite);
|
|
320
|
+
const clangOpt = llvmClangOpt(testCase.suite);
|
|
321
|
+
const artifactBytes = Buffer.from(JSON.stringify(kvmArtifact));
|
|
322
|
+
const hash = sha256();
|
|
323
|
+
hash.update("k-parent-benchmark-llvm-jit-v3\0");
|
|
324
|
+
hash.update(llvmBackendFingerprint());
|
|
325
|
+
hash.update("\0");
|
|
326
|
+
hash.update(runtimeMode);
|
|
327
|
+
hash.update("\0");
|
|
328
|
+
hash.update(clangOpt);
|
|
329
|
+
hash.update("\0");
|
|
330
|
+
hash.update(artifactBytes);
|
|
331
|
+
const exePath = path.join(llvmCacheDir(), `${hash.digest("hex")}.exe`);
|
|
332
|
+
if (!fs.existsSync(exePath)) {
|
|
333
|
+
const tmpPath = path.join(llvmCacheDir(), `${path.basename(exePath)}.${process.pid}.tmp`);
|
|
334
|
+
const llvm = emitLLVMModule(kvmArtifact.kir, {
|
|
335
|
+
relation: kvmArtifact.relation,
|
|
336
|
+
runtimeMode
|
|
337
|
+
});
|
|
338
|
+
compileLLVMToExecutable(llvm, tmpPath, {
|
|
339
|
+
driver: stdioDriverSource({
|
|
340
|
+
inputPattern: canonicalPattern(kvmArtifact.inputPattern),
|
|
341
|
+
outputPattern: canonicalPattern(kvmArtifact.outputPattern)
|
|
342
|
+
}),
|
|
343
|
+
clangOpt
|
|
344
|
+
});
|
|
345
|
+
fs.renameSync(tmpPath, exePath);
|
|
346
|
+
}
|
|
347
|
+
return exePath;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
class PersistentExecutable {
|
|
351
|
+
constructor(exePath) {
|
|
352
|
+
this.exePath = exePath;
|
|
353
|
+
this.child = spawn(exePath, ["--server"], { stdio: ["pipe", "pipe", "pipe"] });
|
|
354
|
+
this.buffer = Buffer.alloc(0);
|
|
355
|
+
this.pending = [];
|
|
356
|
+
this.stderr = [];
|
|
357
|
+
this.closed = false;
|
|
358
|
+
|
|
359
|
+
this.child.stdout.on("data", (chunk) => {
|
|
360
|
+
this.buffer = Buffer.concat([this.buffer, chunk]);
|
|
361
|
+
this.drainOutput();
|
|
362
|
+
});
|
|
363
|
+
this.child.stderr.on("data", chunk => this.stderr.push(chunk));
|
|
364
|
+
this.child.on("error", error => this.fail(error));
|
|
365
|
+
this.child.on("close", (status, signal) => {
|
|
366
|
+
this.closed = true;
|
|
367
|
+
if (status !== 0) {
|
|
368
|
+
const detail = signal == null ? `status ${status}` : `status ${status}, signal ${signal}`;
|
|
369
|
+
this.fail(new Error(`${this.exePath} --server failed with ${detail}\n${Buffer.concat(this.stderr).toString("utf8")}`.trim()));
|
|
370
|
+
} else if (this.pending.length > 0) {
|
|
371
|
+
this.fail(new Error(`${this.exePath} --server closed with ${this.pending.length} pending request(s)`));
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
fail(error) {
|
|
377
|
+
while (this.pending.length > 0) this.pending.shift().reject(error);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
drainOutput() {
|
|
381
|
+
while (this.pending.length > 0 && this.buffer.length >= 4) {
|
|
382
|
+
const length = this.buffer.readUInt32BE(0);
|
|
383
|
+
if (this.buffer.length < 4 + length) return;
|
|
384
|
+
const payload = this.buffer.subarray(4, 4 + length);
|
|
385
|
+
this.buffer = this.buffer.subarray(4 + length);
|
|
386
|
+
this.pending.shift().resolve(Buffer.from(payload));
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
request(inputWire) {
|
|
391
|
+
if (this.closed) {
|
|
392
|
+
return Promise.reject(new Error(`${this.exePath} --server is closed`));
|
|
393
|
+
}
|
|
394
|
+
const header = Buffer.alloc(4);
|
|
395
|
+
header.writeUInt32BE(inputWire.length);
|
|
396
|
+
const frame = Buffer.concat([header, inputWire]);
|
|
397
|
+
return new Promise((resolve, reject) => {
|
|
398
|
+
this.pending.push({ resolve, reject });
|
|
399
|
+
this.child.stdin.write(frame, error => {
|
|
400
|
+
if (error) {
|
|
401
|
+
const index = this.pending.findIndex(entry => entry.resolve === resolve);
|
|
402
|
+
if (index !== -1) this.pending.splice(index, 1);
|
|
403
|
+
reject(error);
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
close() {
|
|
410
|
+
this.child.stdin.end();
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
async function prepareLLVMJITRunner(testCase, relDef, inputVal, state) {
|
|
415
|
+
const { inputPattern } = wireInput(inputVal, relDef);
|
|
416
|
+
const kvmArtifact = backendKVMArtifact(testCase, inputPattern);
|
|
417
|
+
const inputWire = encodeToWire(inputVal, canonicalPattern(kvmArtifact.inputPattern));
|
|
418
|
+
const exePath = compileLLVMExecutable({ kvmArtifact, testCase });
|
|
419
|
+
const runner = new PersistentExecutable(exePath);
|
|
420
|
+
|
|
421
|
+
return {
|
|
422
|
+
async run() {
|
|
423
|
+
const outputWire = await runner.request(inputWire);
|
|
424
|
+
return decodeWire(outputWire).value;
|
|
425
|
+
},
|
|
426
|
+
cleanup() {
|
|
427
|
+
runner.close();
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function prepareKIRPExportRunner(testCase, relDef, inputVal) {
|
|
433
|
+
const { inputPattern } = wireInput(inputVal, relDef);
|
|
434
|
+
const object = backendObjectForCase(testCase);
|
|
435
|
+
return {
|
|
436
|
+
run() {
|
|
437
|
+
return backendKIRP(testCase, inputPattern, object);
|
|
438
|
+
},
|
|
439
|
+
cleanup() {}
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
async function prepareBackendRunner(job, relation, inputVal, state) {
|
|
444
|
+
if (job.mode.kind === "kir-p") {
|
|
445
|
+
return prepareKIRPExportRunner(job.case, relation.relDef, inputVal);
|
|
446
|
+
}
|
|
447
|
+
if (job.mode.kind === "wasm") {
|
|
448
|
+
return prepareWasmRunner(job.case, relation, inputVal, state);
|
|
449
|
+
}
|
|
450
|
+
if (job.mode.kind === "llvm-jit") {
|
|
451
|
+
return prepareLLVMJITRunner(job.case, relation.relDef, inputVal, state);
|
|
452
|
+
}
|
|
453
|
+
return null;
|
|
454
|
+
}
|
|
455
|
+
|
|
109
456
|
async function prepareState(suite) {
|
|
110
457
|
const state = createState();
|
|
111
|
-
|
|
112
|
-
await evaluateInput(`:load ${path.join(root, source)}`, state);
|
|
458
|
+
await evaluateInput(`:load ${suiteSourcePath(suite)}`, state);
|
|
113
459
|
return state;
|
|
114
460
|
}
|
|
115
461
|
|
|
@@ -149,13 +495,14 @@ function prepareRelation(testCase, state) {
|
|
|
149
495
|
const relDef = state.rels[hash];
|
|
150
496
|
if (!relDef) throw new Error(`Relation hash '${hash}' not found`);
|
|
151
497
|
return {
|
|
498
|
+
relHash: hash,
|
|
152
499
|
relDef,
|
|
153
500
|
kvmFunc: lowerToKVM(relDef, testCase.rel)
|
|
154
501
|
};
|
|
155
502
|
}
|
|
156
503
|
|
|
157
|
-
function runOnce(job, prepared) {
|
|
158
|
-
const { relDef, kvmFunc, inputVal, state } = prepared;
|
|
504
|
+
async function runOnce(job, prepared) {
|
|
505
|
+
const { relDef, kvmFunc, inputVal, state, backend } = prepared;
|
|
159
506
|
run.defs = state;
|
|
160
507
|
run_converged.defs = state;
|
|
161
508
|
|
|
@@ -175,6 +522,10 @@ function runOnce(job, prepared) {
|
|
|
175
522
|
});
|
|
176
523
|
}
|
|
177
524
|
|
|
525
|
+
if (job.mode.kind === "kir-p" || job.mode.kind === "wasm" || job.mode.kind === "llvm-jit") {
|
|
526
|
+
return backend.run();
|
|
527
|
+
}
|
|
528
|
+
|
|
178
529
|
throw new Error(`Unknown benchmark mode '${job.mode.name}'`);
|
|
179
530
|
}
|
|
180
531
|
|
|
@@ -184,25 +535,41 @@ async function workerMain() {
|
|
|
184
535
|
const state = await prepareState(job.case.suite);
|
|
185
536
|
const relation = prepareRelation(job.case, state);
|
|
186
537
|
const inputVal = job.case.suite === "ieee" ? ieeeInput(job.case, state) : arithmeticInput(job.case);
|
|
187
|
-
const
|
|
538
|
+
const backend = await prepareBackendRunner(job, relation, inputVal, state);
|
|
539
|
+
const prepared = { ...relation, inputVal, state, backend };
|
|
188
540
|
const setupMs = performance.now() - setupStartedAt;
|
|
189
541
|
|
|
190
542
|
const samples = [];
|
|
191
543
|
let lastResult;
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
544
|
+
let warmupMs = 0;
|
|
545
|
+
let runStartedHeap = process.memoryUsage().heapUsed;
|
|
546
|
+
try {
|
|
547
|
+
const warmupStartedAt = performance.now();
|
|
548
|
+
const warmupResult = await runOnce(job, prepared);
|
|
549
|
+
warmupMs = performance.now() - warmupStartedAt;
|
|
550
|
+
if (warmupResult === undefined) {
|
|
551
|
+
throw new Error("benchmark warmup operation returned undefined");
|
|
552
|
+
}
|
|
553
|
+
runStartedHeap = process.memoryUsage().heapUsed;
|
|
554
|
+
|
|
555
|
+
for (let i = 0; i < job.samples; i++) {
|
|
556
|
+
const startedAt = performance.now();
|
|
557
|
+
lastResult = await runOnce(job, prepared);
|
|
558
|
+
samples.push(performance.now() - startedAt);
|
|
559
|
+
if (lastResult === undefined) {
|
|
560
|
+
throw new Error("benchmark operation returned undefined");
|
|
561
|
+
}
|
|
199
562
|
}
|
|
563
|
+
} finally {
|
|
564
|
+
backend?.cleanup();
|
|
200
565
|
}
|
|
201
566
|
|
|
202
|
-
const resultInfo =
|
|
567
|
+
const resultInfo = job.mode.kind === "kir-p"
|
|
568
|
+
? digestValue(lastResult)
|
|
569
|
+
: digestResult(lastResult, relation.relDef);
|
|
203
570
|
const memory = process.memoryUsage();
|
|
204
571
|
const usage = process.resourceUsage();
|
|
205
|
-
|
|
572
|
+
const payload = {
|
|
206
573
|
status: "ok",
|
|
207
574
|
mode: job.mode.name,
|
|
208
575
|
case: job.case.name,
|
|
@@ -211,22 +578,34 @@ async function workerMain() {
|
|
|
211
578
|
minMs: Math.min(...samples),
|
|
212
579
|
maxMs: Math.max(...samples),
|
|
213
580
|
setupMs,
|
|
581
|
+
warmupMs,
|
|
214
582
|
maxRssMiB: maxRssMiB(usage.maxRSS),
|
|
215
583
|
heapUsedMiB: memory.heapUsed / 1024 / 1024,
|
|
216
584
|
heapDeltaMiB: (memory.heapUsed - runStartedHeap) / 1024 / 1024,
|
|
217
585
|
resultDigest: resultInfo.digest,
|
|
218
586
|
resultBytes: resultInfo.bytes
|
|
219
|
-
}
|
|
587
|
+
};
|
|
588
|
+
const output = JSON.stringify(payload) + "\n";
|
|
589
|
+
if (process.env.K_BENCHMARK_RESULT_PATH) {
|
|
590
|
+
fs.writeFileSync(process.env.K_BENCHMARK_RESULT_PATH, output);
|
|
591
|
+
} else {
|
|
592
|
+
await new Promise((resolve, reject) => {
|
|
593
|
+
process.stdout.write(output, (error) => error ? reject(error) : resolve());
|
|
594
|
+
});
|
|
595
|
+
}
|
|
220
596
|
}
|
|
221
597
|
|
|
222
598
|
function spawnJob(job, timeoutMs) {
|
|
223
599
|
return new Promise((resolve) => {
|
|
600
|
+
const resultDir = fs.mkdtempSync(path.join(os.tmpdir(), "k-benchmark-job-"));
|
|
601
|
+
const resultPath = path.join(resultDir, "result.json");
|
|
224
602
|
const child = spawn(process.execPath, [scriptPath, "--worker"], {
|
|
225
603
|
cwd: root,
|
|
226
604
|
env: {
|
|
227
605
|
...process.env,
|
|
228
606
|
K_BENCHMARK_WORKER: "1",
|
|
229
|
-
K_BENCHMARK_JOB: JSON.stringify(job)
|
|
607
|
+
K_BENCHMARK_JOB: JSON.stringify(job),
|
|
608
|
+
K_BENCHMARK_RESULT_PATH: resultPath
|
|
230
609
|
},
|
|
231
610
|
stdio: ["ignore", "pipe", "pipe"]
|
|
232
611
|
});
|
|
@@ -246,6 +625,10 @@ function spawnJob(job, timeoutMs) {
|
|
|
246
625
|
child.stderr.on("data", chunk => { stderr += chunk.toString("utf8"); });
|
|
247
626
|
child.on("close", (code, signal) => {
|
|
248
627
|
clearTimeout(timer);
|
|
628
|
+
const resultText = fs.existsSync(resultPath)
|
|
629
|
+
? fs.readFileSync(resultPath, "utf8")
|
|
630
|
+
: stdout;
|
|
631
|
+
fs.rmSync(resultDir, { recursive: true, force: true });
|
|
249
632
|
if (timedOut) {
|
|
250
633
|
resolve({ status: "timeout", mode: job.mode.name, case: job.case.name, timeoutMs });
|
|
251
634
|
return;
|
|
@@ -262,13 +645,13 @@ function spawnJob(job, timeoutMs) {
|
|
|
262
645
|
return;
|
|
263
646
|
}
|
|
264
647
|
try {
|
|
265
|
-
resolve(JSON.parse(
|
|
648
|
+
resolve(JSON.parse(resultText.trim().split("\n").at(-1)));
|
|
266
649
|
} catch (error) {
|
|
267
650
|
resolve({
|
|
268
651
|
status: "failed",
|
|
269
652
|
mode: job.mode.name,
|
|
270
653
|
case: job.case.name,
|
|
271
|
-
stderr: `Could not parse worker output: ${error.message}\n${
|
|
654
|
+
stderr: `Could not parse worker output: ${error.message}\n${resultText}\n${stderr}`
|
|
272
655
|
});
|
|
273
656
|
}
|
|
274
657
|
});
|
|
@@ -286,7 +669,7 @@ async function runPool(jobs, concurrency, timeoutMs) {
|
|
|
286
669
|
results.push(result);
|
|
287
670
|
const label = `${result.mode}/${result.case}`;
|
|
288
671
|
if (result.status === "ok") {
|
|
289
|
-
console.log(`[ok] ${label} ${formatMs(result.avgMs)} ms avg, ${formatMiB(result.maxRssMiB)} MiB max RSS`);
|
|
672
|
+
console.log(`[ok] ${label} ${formatMs(result.avgMs)} ms avg, ${formatMs(result.warmupMs)} ms warmup, ${formatMiB(result.maxRssMiB)} MiB max RSS`);
|
|
290
673
|
} else {
|
|
291
674
|
console.log(`[${result.status}] ${label}`);
|
|
292
675
|
}
|
|
@@ -322,7 +705,7 @@ function printSummary(document) {
|
|
|
322
705
|
console.log(`parallel jobs: ${document.parallelJobs}`);
|
|
323
706
|
console.log(`results file: ${path.relative(root, resultsPath)}`);
|
|
324
707
|
console.log("-------------------------------------------------------------");
|
|
325
|
-
console.log(`${pad("case", 20)} ${pad("mode", 24)} ${pad("status", 8)} ${pad("avg ms", 10)} ${pad("min", 9)} ${pad("max", 9)} ${pad("rss MiB", 9)} ${pad("heap MiB", 9)} digest`);
|
|
708
|
+
console.log(`${pad("case", 20)} ${pad("mode", 24)} ${pad("status", 8)} ${pad("avg ms", 10)} ${pad("min", 9)} ${pad("max", 9)} ${pad("warmup", 9)} ${pad("rss MiB", 9)} ${pad("heap MiB", 9)} digest`);
|
|
326
709
|
for (const result of document.results) {
|
|
327
710
|
console.log([
|
|
328
711
|
pad(result.case, 20),
|
|
@@ -331,6 +714,7 @@ function printSummary(document) {
|
|
|
331
714
|
pad(formatMs(result.avgMs), 10),
|
|
332
715
|
pad(formatMs(result.minMs), 9),
|
|
333
716
|
pad(formatMs(result.maxMs), 9),
|
|
717
|
+
pad(formatMs(result.warmupMs), 9),
|
|
334
718
|
pad(formatMiB(result.maxRssMiB), 9),
|
|
335
719
|
pad(formatMiB(result.heapUsedMiB), 9),
|
|
336
720
|
result.resultDigest || ""
|
|
@@ -359,7 +743,7 @@ async function main() {
|
|
|
359
743
|
const selectedBenchmarkModes = selectedModes();
|
|
360
744
|
const samples = process.env.BENCHMARK_SAMPLES ? Number(process.env.BENCHMARK_SAMPLES) : 3;
|
|
361
745
|
const timeoutMs = process.env.BENCHMARK_TIMEOUT_MS ? Number(process.env.BENCHMARK_TIMEOUT_MS) : 2 * 60 * 1000;
|
|
362
|
-
const requestedJobs = process.env.BENCHMARK_JOBS ? Number(process.env.BENCHMARK_JOBS) :
|
|
746
|
+
const requestedJobs = process.env.BENCHMARK_JOBS ? Number(process.env.BENCHMARK_JOBS) : 1;
|
|
363
747
|
const concurrency = Math.max(1, Math.min(requestedJobs, os.availableParallelism(), selected.length * selectedBenchmarkModes.length));
|
|
364
748
|
|
|
365
749
|
if (!Number.isInteger(samples) || samples <= 0) throw new Error("BENCHMARK_SAMPLES must be a positive integer");
|