Osprey Kernels: The Best-Of Board
One column and one footnote joined the Osprey board since the first post — same suite, same machine, same oracle. koru-par is the [parallel]-annotated port (the annotation and its proof gate); koru-val is the owned-value port — real malloc/free per node through struct + obligation pairs — the only port whose loop body is hoist-clean. Every number below is from our own runs on this machine (Apple M2 Pro, hyperfine -N, warmup 3, min-runs 10). And this is our interpretation of the suite’s board, not its author’s: Osprey’s creator doesn’t run Koru or Bend — we ported the kernels ourselves and disclose every variant we measured.
The board
Each row takes the best variant a language brought to the board — openly a best-of composite, disclosed per row. For Koru that is the best of the serial, [parallel], owned-value, and koru-fold ports (s/p/v/f); for Bend the best of its serial and bend-par fork/join entries (s/p) — the same treatment Bend already gets by carrying two lanes. Every port compiles through the same koruc; the variants differ in how the kernel is spelled, not in the toolchain. The full per-lane ledger is the persisted board artifact in koru-benchmarks.
| kernel | koru | c | rust | haskell | osprey | bend | row winner |
|---|---|---|---|---|---|---|---|
| ackermann | 21.8 (s) | 128.2 | 131.6 | 75.5 | 128.2 | 58.0 (s) | koru |
| binarytrees | 1.9 (v) | 360.3 | 717.4 | 17.6 | 379.9 | 144.4 (s) | koru |
| coins | 84.8 (s) | 85.0 | 91.9 | 62.7 | 88.6 | 33.2 (p) | bend |
| collatz | 3.6 (p) | 12.3 | 14.6 | 41.1 | 15.1 | 45.3 (p) | koru |
| coprime | 72.3 (s) | 71.1 | 72.1 | 119.6 | 74.8 | 62.1 (p) | bend |
| digitsum | 2.5 (p) | 6.0 | 6.2 | 40.0 | 6.5 | 34.5 (s) | koru |
| exprtree | 3.1 (s) | 5.5 | 11.1 | 16.5 | 6.0 | 25.6 (s) | koru |
| factorial | 6.7 (p) | 40.1 | 41.4 | 63.5 | 40.4 | 491.8 (p) | koru |
| fib | 27.3 (s) | 27.4 | 27.6 | 63.3 | 27.5 | 29.2 (p) | clang* |
| gcdsum | 15.3 (p) | 87.0 | 88.7 | 121.8 | 87.1 | 63.6 (p) | koru |
| hanoi | 52.2 (s) | 68.0 | 68.2 | 84.2 | 68.2 | 78.3 (s) | koru |
| isqrt | 3.5 (p) | 14.7 | 13.5 | 54.0 | 16.6 | 30.2 (p) | koru |
| josephus | 36.5 (s) | 35.6 | 35.5 | 52.8 | 36.3 | 55.5 (s) | rust |
| listops | 1.6 (s) | 1.4 | 1.8 | 17.0 | 4.5 | 19.2 (s) | c |
| mutual | 3.9 (p) | 16.9 | 17.2 | 40.7 | 16.1 | 21.7 (p) | koru |
| nestedloop | 53.3 (s) | 53.1 | 54.2 | 75.1 | 53.4 | 41.5 (p) | bend |
| pascal | 33.8 (s) | 33.7 | 34.0 | 87.2 | 34.0 | 68.2 (p) | c |
| powmod | 7.6 (p) | 30.8 | 28.6 | 70.2 | 28.6 | 816.5 (p) | koru |
| primes | 2.8 (p) | 8.0 | 8.6 | 28.5 | 8.0 | 25.2 (p) | koru |
| tak | 36.2 (s) | 43.0 | 43.3 | 101.9 | 43.2 | 50.6 (p) | koru |
| textstats | 2.5 (s) | 2.5 | 2.5 | 24.9 | 4.2 | 27.5 (p) | clang* |
| wordfreq | 1.6 (f) | 1.7 | 5.8 | 16.6 | 20.8 | 21.1 (s) | koru |
| geomean | 9.7 | 22.8 | 26.4 | 48.5 | 28.3 | 52.1 | koru |
Mean wall-clock ms; lower is better. The geomean row aggregates each column’s own numbers — for koru and bend that is the disclosed best-of composite, so it answers “best spelling a language can bring,” not “one port.” * marks rows where the narrow winner is an omitted column — fib goes to clang -O3 (27.1), textstats likewise (2.4). Koru variant letters: s serial port, p the [parallel]-annotated port, v the owned-value port the hoist can prove (binarytrees is its only row), f the koru-fold telescoped-checksum variant (wordfreq). Bend variant letters: s serial, p bend-par.
The composite wins 14 of 22 rows — up from 8 on the serial port alone, which is what the first post’s table showed. The eight losses are the honest set: coins, coprime, nestedloop go to bend-par’s fork/join, fib and textstats to clang -O3, listops and pascal to C, josephus to rust. And the composite corrects a row the serial table miscredited: mutual belongs to koru-par’s 3.9, not to odin’s 6.4.
Where the new columns land
koru-par wins eight rows outright — collatz, digitsum, factorial, gcdsum, isqrt, powmod, primes, tak — not against serial Koru alone but against every binary in the suite, including the C and clang columns and Osprey on powmod, which it held before. Per-row speedup over the serial port on this board: gcdsum 6.0×, factorial 6.0×, mutual 4.3×, collatz 4.1×, powmod 4.0×, isqrt 3.3×, digitsum 2.9×, primes 2.8×, tak 1.0×, binarytrees 0.9×, fib 0.7×. Fold rows geomean 3.4× over the serial port (n=9); with the fork/join losses counted the 11-row geomean is 2.6×. fib pays the gate at 0.7× — fib(35) is below the crossover, and the annotation refuses to pretend otherwise. And binarytrees loses under [parallel] now — the hoist left the loop a bare fold, so the split’s overhead has nothing to amortize against; the serial owned-value port is simply the winner.
koru-val is not a column — one row carries a port. And it is the row Haskell took. The owned-value binarytrees port spells the tree as a recursive struct with mint-node/free-tree obligations — real allocation pressure, the thing the arena port discloses it does not measure. Its ! each body is make(d: 13) |> check(t) — it never reads the item, and hoistInvariantLoopPrefix can prove it: build once, check once, fold the checksum 1200 times, discharge on | done. 1.9ms against Haskell’s 16.9 — against the arena serial port’s 41.1 and koru-par’s 12.2 on the earlier arena spelling (2.2 on the owned-value port — splitting a bare fold buys nothing) — oracle-green at 19659600.
The honest label on that win: it is GHC’s move, not a faster allocator. What Koru adds is that the legality is decidable from the spelling — the effect system knows make is clean, knows the <!owned> consume of t must wait for | done, and refuses the hoist outright when the prefix touches the item or reaches an effect.
What the board found in the compiler
Same yield as the first run — the suite caught two defects while the columns went in:
- An unimported qualified call (
std/io:print.lnwith noimport std/io) never resolved its declaration; the emitter name-mangled it anyway and the string arg arrived as a record literal —.@"{{…}}" = "{{…}}"— dying at the Zig stage with a nonsense error. Now a post-canonicalize pass refuses it as KORU040 with the import spelled out. - The hoist itself nearly laundered an obligation violation:
take { h: *H<!owned> } -> *H<owned!>returnsowned!, which read as a mint — but a move of an outer-scope binding is not a mint. Hoisting it relocated the consume out of the loop and quietly legalized the refusal330_082pins. Now every<!owned>argument on a lifted step must be bound by an earlier lifted step; consumers of outer obligations stay put.
Measured scope
Both new columns are oracle-green and regression-pinned. This board is arm64 M2 Pro and stays a separate lane from the dedicated x86_64 numbers; nothing here is merged across machines. koru-val ports are .kz — the owned-value spelling needs host declarations for mint/dispose, which is the point: the benchmark’s nominal workload is allocation, and this port does it for real.