Osprey Kernel Suite: Koru Ports All 22 Kernels
The Osprey benchmark suite is twenty-two naive compute kernels — integer recursion, folds, and data-structure stress — each vendored with reference implementations and an integer oracle. Every one now has a Koru port, and every port is oracle-green. Every number below is from our own runs on this machine (Apple M2 Pro, hyperfine -N, warmup 3, min-runs 10); the suite’s published numbers are theirs.
The board
| kernel | koru | c | rust | haskell | osprey | bend | bend-par | row winner |
|---|---|---|---|---|---|---|---|---|
| ackermann | 22.8 | 132.5 | 136.2 | 78.6 | 132.4 | 65.2 | — | koru |
| binarytrees | 41.4 | 371.6 | 721.1 | 17.5 | 394.1 | 145.8 | 568.0 | haskell |
| coins | 85.8 | 86.0 | 96.7 | 65.5 | 88.9 | 64.6 | 36.0 | bend-par |
| collatz | 15.2 | 12.1 | 14.3 | 41.8 | 15.5 | 50.0 | 44.1 | c |
| coprime | 73.9 | 72.8 | 74.5 | 121.9 | 76.4 | 94.9 | 67.3 | bend-par |
| digitsum | 7.5 | 6.1 | 6.3 | 41.3 | 7.0 | 37.2 | 44.1 | c* |
| exprtree | 3.1 | 5.9 | 11.7 | 18.1 | 6.5 | 25.8 | — | koru |
| factorial | 40.7 | 41.3 | 41.0 | 64.1 | 40.9 | 620.1 | 499.0 | koru |
| fib | 28.4 | 29.0 | 28.2 | 65.1 | 28.7 | 67.1 | 27.7 | c* |
| gcdsum | 86.5 | 85.3 | 85.4 | 116.0 | 84.5 | 107.5 | 64.0 | bend-par |
| hanoi | 52.7 | 68.6 | 68.5 | 85.3 | 69.0 | 79.0 | — | koru |
| isqrt | 11.5 | 14.7 | 13.0 | 53.0 | 17.0 | 40.7 | 30.9 | koru |
| josephus | 36.6 | 35.9 | 35.9 | 53.2 | 35.7 | 57.2 | — | c* |
| listops | 1.7 | 1.4 | 1.7 | 16.8 | 4.4 | 19.3 | — | c |
| mutual | 17.1 | 17.2 | 17.4 | 41.2 | 16.7 | 26.2 | 26.1 | c3 |
| nestedloop | 55.1 | 55.1 | 54.0 | 78.3 | 55.3 | 63.9 | 43.3 | bend-par |
| pascal | 34.8 | 34.2 | 34.8 | 89.2 | 34.4 | 253.8 | 59.7 | c |
| powmod | 30.1 | 29.7 | 29.6 | 68.8 | 29.5 | 1529.5 | 821.7 | osprey |
| primes | 8.1 | 8.0 | 8.7 | 29.1 | 8.0 | 28.8 | 26.3 | c / osprey |
| tak | 36.6 | 43.4 | 43.5 | 102.1 | 43.2 | 102.6 | 47.6 | koru |
| textstats | 2.3 | 2.4 | 2.5 | 22.1 | 3.9 | 28.6 | 27.3 | koru |
| wordfreq | 4.0 (1.5†) | 1.7 | 5.8 | 16.7 | 20.6 | 18.4 | 20.0 | koru-fold† |
Mean wall-clock ms; lower is better. * marks rows where the narrow winner is the clang -O3 column rather than the cc -O2 column shown (fib 27.6, josephus 35.5, digitsum 6.0). mutual’s winner is c3 at 6.7 — column omitted for width. †koru-fold is wordfreq’s disclosed telescoped-checksum variant (1.5ms); the faithful map port is the 4.0 figure.
Where Koru stands
Reduced to geomeans — every column whole-process under the same harness, so the ratios are clean — Koru runs the suite 1.48× faster than Osprey, 1.37× faster than Rust, and 1.19× faster than C (geometric mean of per-kernel ratios against Koru’s 1.00× baseline, n=22 for all three). Further out: 2.52× over Haskell, 2.75× over bend-par (n=17 — five kernels have no parallel port), and 3.63× over sequential Bend, though powmod’s 52× outlier carries much of that; the per-row table is the honest picture there.
Koru leads eight rows outright — ackermann by ~3× over the next binary (the kernel is nothing but call-stack pressure, and the emitted recursion is lean), exprtree by ~2×, hanoi, isqrt, tak, textstats, factorial (a hair over Osprey’s own entry), and wordfreq through the fold column. Most of the remaining rows sit at C parity: fib, pascal, powmod, primes, josephus, nestedloop, coins, coprime, gcdsum, collatz, listops land within a few percent of cc -O2.
The losses are honest and named: bend-par takes coins, coprime, gcdsum, and nestedloop — fork-join parallelism over heavy leaves is a real win no sequential binary can match; haskell takes binarytrees — a garbage-collected allocator is exactly what that kernel measures; c3 takes mutual; plain C keeps collatz, digitsum, listops, and pascal by single-digit margins; osprey edges powmod on its home board. Sequential Bend wins no rows — its 48-bit Nat costs twenty-to-thirty× the arithmetic on powmod’s mulmod-heavy path, and its evaluator tax shows on every scalar-recursion kernel.
The last three kernels
Until this week the board carried three ports marked “blocked on an absent language feature”: binarytrees, exprtree, and listops are recursive union types — Tree = Leaf | Node { left, right } — which Koru does not spell. The LangArena work had already proven the pattern that dissolves the wall: a flat arena. A tree becomes rows in a std/grid, children become preorder-arithmetic indexes — node h at depth d holds left = h+1, right = h + 2^d — and the traversal chases the stored indexes so the tree materializes honestly rather than being folded away:
std/grid:new(arena, size: 16383) { left: -1[i32], right: -1[i32] }
pub tor build { depth: i64, h: i64 } -> i64
build = if(depth > 0)
| then |> std/grid:stored { arena[@as(usize, @intCast(h))].left: @as(i32, @intCast(h + 1)),
arena[@as(usize, @intCast(h))].right: @as(i32, @intCast(h + (@as(i64, 1) << @as(u6, @intCast(depth))))) }: _
|> build(depth: depth - 1, h: h + 1): _
|> build(depth: depth - 1, h: h + (@as(i64, 1) << @as(u6, @intCast(depth)))): _ -> h
| else |> std/grid:stored { arena[@as(usize, @intCast(h))].left: -1,
arena[@as(usize, @intCast(h))].right: -1 }: _ -> h The disclosure each port carries: an arena allocated once and overwritten per iteration does not measure allocation pressure — it measures build-and-traverse. That is the trade the benchmark’s faithfulness rule allows, stated in the file, rather than a hidden advantage.
What the suite found in the compiler
Completing the column surfaced two real emission gaps, both recorded:
- A
|>continuation line at equal indentation to! eachparses as a sibling step after the loop — the ops silently execute once, outside theeachbody. No diagnostic; the wrong program compiles clean.exprtree’s first compile shipped exactly this and produced a plausible, wrong checksum. - A tor invocation missing an argument emits
.{ .idx = _ }into the Zig layer instead of a Koru diagnostic — the bare_then fails the Zig compile, leaking generated code to the programmer.
Neither is a benchmark-shaped complaint; both are compiler findings any user can hit, the same yield the LangArena grind produced from the kernel emitter.
The Osprey column, honestly
The Osprey numbers above are built from the suite’s own sources at commit 54f385a — the version the vendored programs were written for. Current upstream main refuses every vendored kernel: arithmetic is now an effect that must be handled, and the case files predate the requirement (unhandled effect operations at program entry: Arith.overflow). Their benchmark suite doesn’t compile on their latest main — a finding for upstream, not a dig; we’ve sent it their way.
The Bend column is Bend 2.0.5, the current release — bend/ is the faithful sequential port, bend-par/ the same algorithms forked where a fork is meaningful.