Generated bypython benchmarks/run_benchmarks.py --report
(median of 3 runs per program, including process startup; Apple Silicon;
CPython 3.13 / Rust 1.89 release / clang -O2). For methodology and
per-program attribution see benchmarks/README.md.
Reproduce:python benchmarks/run_benchmarks.py --report.
| Program | Silu Interpreter | CPython | Silu Native | Rust | Native vs Rust |
|---|---|---|---|---|---|
| arith | 5.532s | 0.080s | 0.0033s | 0.0029s | 0.9Γ |
| dict | 0.269s | 0.034s | 0.0022s | 0.0028s | 1.3Γ |
| fannkuch | 0.049s | 0.032s | 0.0026s | 0.0025s | 0.9Γ |
| fib | 1.814s | 0.036s | 0.0025s | 0.0028s | 1.1Γ |
| float | 3.791s | 0.052s | 0.0027s | 0.0028s | 1.0Γ |
| jsonparse | 1.747s | 0.047s | 0.0074s | 0.0036s | 0.5Γ |
| jsonparse_json | 1.816s | 0.042s | 0.0079s | β | (Json twin, see RFC 7c) |
| knucleotide | 15.614s | 0.889s | 0.0110s | 0.0148s | 1.3Γ |
| list | 0.479s | 0.035s | 0.0024s | 0.0024s | 1.0Γ |
| mandelbrot | 1.214s | 0.041s | 0.0024s | 0.0027s | 1.2Γ |
| nbody | 61.200s | 0.214s | 0.0057s | 0.0054s | 0.9Γ |
| spectral | 71.253s | 0.288s | 0.0074s | 0.0047s | 0.6Γβ» |
| string | 0.553s | 0.049s | 0.0030s | 0.0022s | 0.7Γ |
| trees | 0.873s | 0.031s | 0.0032s | 0.0034s | 1.1Γ |
| wordcount | 6.740s | 0.088s | 0.0233s | 0.0137s | 0.6Γ |
**Geometric mean: 0.9Γ vs Rust (slightly behind overall) | 15.6Γ vs CPython | 515Γ vs interpreter**; startup baseline: silu-int 0.038s / cpython 0.028s / binary 0.0041s. 10 of 15 rows at β₯0.9Γ (dict/knucleotide lead at 1.3Γ).
Runtime slimming note: LTO + -Wl,-dead_strip strips unreferenced runtime functions at symbol granularity (fib binary 35KB vs 113KB monolithic). The "per-program runtime selection" item is closed β the linker does it for free.
jsonparse five-way comparison (quiet environment, 31 runs, 87KB document, all outputs byte-identical):
| Plane | min | Notes |
|---|---|---|
C (clang -O2, isomorphic tree+realloc, benchmarks/c/jsonparse.c) | 2.7ms | Median unstable (10-27ms swing, malloc-sensitive) |
| Rust (serde_json shape) | 3.6ms | Stable |
| Silu class twin | 6.5ms | Stable |
| Silu Json twin | 6.7ms | Stable |
| CPython (pycompat) | 42ms | |
| Silu interpreter | 1735ms |
LTO erased RFC 7c's 1.2ms: the Json twin now matches the class twin (unwrap/tag dispatch inlined into hot loops by -flto). The remaining gap vs Rust/C still attributes to construction (~1.9ms, the J1 three-layer decomposition).
β» spectral high-variance note (v1.14.0 retest): the same binary measured 4.5β7.4ms across three environments (millisecond-level programs swing percentage wildly on ms jitter); and the link-strategy cost was isolated β the same .ll compiled monolithic via clang: 3.86ms (1.17Γ overtakes Rust), while siluc's "user.o + rt.o" split link: 5.58ms (0.81Γ): the cross-TU boundary blocks runtime-function inlining. LTO monolithic verified at 3.89ms.
Landed (f475966): LTO link is now the siluc default β rt.o stores LLVM bitcode (one-time 0.15s), the user module compiles to bitcode, linking runs clang -flto -O2 -Wl,-dead_strip (no conflicts). Cost: +20ms per link (0.05 vs 0.03s), siluc total warm stays 0.11-0.12s. Benefit (quiet-environment hand-timed, vs Rust): spectral 4.66β4.15ms, fib 1.08Γ, dict 1.04Γ, arith 1.03Γ; startup baseline halved (4.1β2.1ms).
| Compiler | fib (s/program) | Notes |
|---|---|---|
| siluc (C driver + siluc1) | 0.136s | One command, zero Python; runtime .o cached |
| rustc -C opt-level=3 | 0.128s | Direct call, no cache (three consecutive compiles constant + zero disk residue, verified) |
| Python pipeline (selfhost_build) | 0.178s | Orchestration is Python; the compiler itself is still siluc1 |
| Python compiler (silu build) | 0.497s | Full Python implementation; for development/debugging |
| clang -O2 (C twin) | 0.06s | Reference |
siluc vs rustc gap is 1.06Γ; the remainder is entirely the clang user-segment + link proper (the pipeline's ~0.12s is the clang floor). cargo's cache is artifact-level (cold 0.274s / warm 0.050s), similar to our .o cache but coarser-grained.
| Date | Milestone | jsonparse | Full-suite geomean vs Rust |
|---|---|---|---|
| 10-14 | Baseline (pre-v1.0) | 78ms (0.06Γ) | 0.9Γ (Rust slightly ahead) |
| 10-24 | Print specialization: static scalars/strings skip pyobj boxing | β | β |
| 10-25 | dead_strip linking (binary size 3Γβ) | β | β |
| 10-25 | Match-class pattern narrowing: case JInt(n) payload static binding | 78β75ms | β |
| 10-25 | Borrowed str parameters: fixed per-recursion deep copy of the whole document | 78.7β8.3ms (9.5Γ) | β |
| 10-25 | Fresh report (all fixes) | 10.7ms (0.4Γ) | 0.9Γ (parity range) |
Two attributed remaining gaps:
materialization pooling)**: three-layer attribution (construction 12% / materialization the bulk) and fix chain β v1.11.4's pool was initially attached to the zero-call rawstr_char_at (its 0.8ms gain was noise, corrected); the real hot path was s[i]'s viewβRAWSTR materialization (rawstr_new_copy: 2 mallocs + memcpy + SIMD code-point count, ~62ns/char). This round connected materialization to rawstr_char1 (one-byte code points serve from a 256-entry shared pool, cap=-1 immortal sentinel): scan probe 9.9β4.7ms, **at parity with the hand-written ord() idiom (62β~5ns/char), jsonparse 9.8β8.0-8.5ms. Inline strategy measured and rejected**: clang -O2 already inlines small runtime functions (verified in assembly); the remaining single char1 call with alwaysinline actually regressed 4.64β4.91ms (icache damage). **Remaining gap β construction cost 1.9ms (pure-runtime 5.2 vs 3.5ms, gap 1.7ms)** β sum-types (RFC S1-S4) promoted from "secondary" to the closing critical path: post-materialization construction share rose from 12% to ~24%.
eliminated w = vocab[i]'s pyobj boxing (.ll call sequence verified), but exposed a deeper layer: rawlist_str_get has copy semantics (slots own copies) β each read mallocs a new block + frees the old slot, two heap operations actually costlier than single boxing. A/B measured (old .ll 21.2ms vs new 24.3ms, 15-run min each). The fix = borrowed reads (immutable-element subscripts return borrowed pointers) or buffer reuse (rawstr_copy_into, Rust's clone_from approach) β the same immutable-value copy-semantics family as jsonparse's char access, treatable together.
interpreter/CPython/compiled-artifact/Rust-twin outputs are identical; otherwise the row is marked MISMATCH and excluded.
constructors, differing from bare CPython semantics); Rust twins are hand-mirrored per program and output-compared.
(4.3ms silu / 2.3ms rust empty program).
sample profiling (example:jsonparse's 70ms hot zone was sampled and pinned to parameter deep-copy; fixed to 8.3ms).