Open and answer this issue on GitHub.
ready · opened 2026-10-26 · updated 2026-10-26 · 0 comments
print("Hallo Welt") is the smallest program there is, so what it costs is pure overhead: nothing in it computes. Python is the usual yardstick for a scripting start. The goal of this issue: a Nikaia Hello World costs fewer instructions than Python’s Hello World, in every way a program is started.
fn main() {
print("Hallo Welt")
}
A program is started in four ways, and each has its own overhead:
| # | Scenario | What happens |
|---|---|---|
| 1 | a built binary (nikaia build, then run target/nikaia/bin/<name>) |
the process starts, nothing else |
| 2 | nikaia run f.nika, warm (same file, unchanged, second run) |
read, check, lower, look the compiled program up in the cache, start it |
| 3 | nikaia run f.nika after an edit |
the same, plus the compile of the changed Rust |
| 4 | nikaia run of a program never seen |
the same as 3 in a fresh cache entry, and the first run on a machine also fills the bundle (run_bundle) |
Rough shell timings from one session (a loop of runs on a shared machine, so an indicator only, not a measurement in the sense of this repository; the figures below are the method, not numbers to quote):
dev profile of nikaia build.nikaia run asks rustc --version on every start (orchestrator::project::rustc_version, crates/orchestrator/src/project.rs), to key the cache (crates/nikaia/src/run_fast.rs, compile). The call goes through the rustup proxy, which starts the real compiler; calling the toolchain’s rustc directly is a fraction of that. The rest is the in-process front end (parse, check, lower) before the cache lookup.~/.cache/nikaia/run/<name>-<hash>/ and a new hash means a new entry. What goes into the hash (path, content, or both) is not yet checked.A run of nikaia run is a program too, so scenario 2 is the one a user meets most (every edit-run loop, every script).
A language that wants to be used for scripts and tools (scripts/*.nika is the rule, CLAUDE.md) is judged by what nikaia run script.nika costs when the script does nothing. Today a Python script starts faster, which is the wrong way round for the stated purpose.
By CLAUDE.md’s rule: instructions and mispredicted branches from callgrind, never seconds; no figure in seconds goes into a document. The comparison is Python’s print("Hallo Welt") against Nikaia’s, both counted by valgrind --tool=callgrind --branch-sim=yes, children included (--trace-children=yes, since nikaia run starts the program and a compiler probe). Same output first, byte for byte. benches/brc/brc.sh is the pattern; the result is a small table in a README of its own under benches/, replaced when re-measured.
benches/hello/: a script that counts instructions for the four scenarios and for Python, checks the outputs are equal, and prints the table. It is the acceptance test of this issue and the baseline for the steps below.rustc --version child, which the front end, which the cache lookup and the start. Understand it before changing anything. Check the hash in the cache directory name (what goes in).rustc --version off the warm path. Ask for the version once per toolchain, not once per run: resolve the toolchain’s own rustc (as run_fast::compile already does for the compile) and key the cache on what is cheap to read (the toolchain’s path and modification time, or a stamp file next to the cache entry), asking the compiler only when that stamp changed. rustc_version() is also used for the check of the installed version (installed_rustc); keep that answer, from the same stamp.nikaia binary are unchanged, check and lower need not run at all: a stamp of (source, dependencies, compiler version) on the cache entry says the compiled program is current. Lowering from cache is already measured as a part; the question is how little a hit has to do. Diagnostics (warnings the program would show) are replayed from the stored messages, as run_fast already does.exec it instead of spawning and waiting where the platform allows, so the process that remains is the program itself.nikaia build’s Hello World with the release profile and with the least start-up work the language adds (no initialisation that a program that uses nothing does not need). Compare its count with Python’s and with a C puts as the floor.The commit that builds the last step says Fixes #N. A change to crates/ raises the version (the supervisor’s, see CLAUDE.md); the benchmark under benches/ does not.