🏠 HomeBenchmarksMemorySum Types RFCSymbolic RoadmapC Interop
EN δΈ­ζ–‡

Speed Benchmarks: Silu vs CPython vs Rust (2026-10-25, v1.9.0)

Generated by python 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.

1. Runtime Speed: 15 programs (incl. Json twin) Γ— 4 planes

Program Silu Interpreter CPython Silu Native Rust Native vs Rust
arith5.532s0.080s0.0033s0.0029s0.9Γ—
dict0.269s0.034s0.0022s0.0028s1.3Γ—
fannkuch0.049s0.032s0.0026s0.0025s0.9Γ—
fib1.814s0.036s0.0025s0.0028s1.1Γ—
float3.791s0.052s0.0027s0.0028s1.0Γ—
jsonparse1.747s0.047s0.0074s0.0036s0.5Γ—
jsonparse_json1.816s0.042s0.0079sβ€”(Json twin, see RFC 7c)
knucleotide15.614s0.889s0.0110s0.0148s1.3Γ—
list0.479s0.035s0.0024s0.0024s1.0Γ—
mandelbrot1.214s0.041s0.0024s0.0027s1.2Γ—
nbody61.200s0.214s0.0057s0.0054s0.9Γ—
spectral71.253s0.288s0.0074s0.0047s0.6Γ—β€»
string0.553s0.049s0.0030s0.0022s0.7Γ—
trees0.873s0.031s0.0032s0.0034s1.1Γ—
wordcount6.740s0.088s0.0233s0.0137s0.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.7msMedian unstable (10-27ms swing, malloc-sensitive)
Rust (serde_json shape)3.6msStable
Silu class twin6.5msStable
Silu Json twin6.7msStable
CPython (pycompat)42ms
Silu interpreter1735ms

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).

2. Compile Speed: siluc vs rustc vs clang

Compiler fib (s/program) Notes
siluc (C driver + siluc1)0.136sOne command, zero Python; runtime .o cached
rustc -C opt-level=30.128sDirect call, no cache (three consecutive compiles constant + zero disk residue, verified)
Python pipeline (selfhost_build)0.178sOrchestration is Python; the compiler itself is still siluc1
Python compiler (silu build)0.497sFull Python implementation; for development/debugging
clang -O2 (C twin)0.06sReference

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.

3. Speed Evolution Trail (what worked)

Date Milestone jsonparse Full-suite geomean vs Rust
10-14Baseline (pre-v1.0)78ms (0.06Γ—)0.9Γ— (Rust slightly ahead)
10-24Print specialization: static scalars/strings skip pyobj boxingβ€”β€”
10-25dead_strip linking (binary size 3×↓)β€”β€”
10-25Match-class pattern narrowing: case JInt(n) payload static binding78β†’75msβ€”
10-25Borrowed str parameters: fixed per-recursion deep copy of the whole document78.7β†’8.3ms (9.5Γ—)β€”
10-25Fresh 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.

4. Methodology Notes

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).

jsonparse's 70ms hot zone was sampled and pinned to parameter deep-copy; fixed to 8.3ms).