Benchmarks
Every number, with its machine and method.
These results come from the benchmark suites in the Alice repository. Every program has a twin in the other language with the same algorithm and the same checksum. Numbers from different machines are never compared directly.
Bend 2 runtime suite
The eight workloads behind the numbers on bend-lang.com, ported 1:1 to Alice: same algorithm, same fork tree, same checksum as Bend's own C twin, at Bend's big sizes.
Machine
x86-64 build box, 32 cores, 61 GB, Arch Linux, .NET 10.0.114, clang 23 with libomp. Shared with other jobs during the run (load average 8-10), so read ratios within 1.2x as equal.
Compiler: Alice development build after 0.14.0
Method
- Cold-process wall time: startup, JIT and the work together (hyperfine -N, median of 5 after one warm-up).
- Serial: 1 core, pinned with taskset; Alice also runs with DOTNET_PROCESSOR_COUNT=1.
- Parallel: all 32 cores. Alice forks the pure recursive pairs itself, with no annotations. C is Bend's main.c with an OpenMP path added (clang -O3 -march=native -fopenmp).
- Every Alice checksum equals the C checksum.
Source: benchmarks/bend2/README.md
| Workload | Alice | C (clang -O3) | Alice / C (clang -O3) |
|---|---|---|---|
| bfs | 10,843 | 6,441 | 1.68x |
| editdist | 11,348 | 8,774 | 1.29x |
| gameoflife | 14,296 | 15,807 | 0.90x |
| hashmap | 1,464 | 1,426 | 1.03x |
| kmeans | 32,904 | 2,809 | 11.71x |
| lexer | 2,097 | 1,513 | 1.39x |
| mandelbrot | 3,712 | 2,462 | 1.51x |
| merkle | 11,963 | 6,880 | 1.74x |
| Workload | Alice | C (OpenMP) | Alice / C (OpenMP) |
|---|---|---|---|
| bfs | 839 | 301 | 2.79x |
| editdist | 726 | 507 | 1.43x |
| gameoflife | 1,123 | 637 | 1.76x |
| hashmap | 162 | 87 | 1.86x |
| kmeans | 2,774 | 269 | 10.31x |
| lexer | 201 | 164 | 1.23x |
| mandelbrot | 349 | 183 | 1.91x |
| merkle | 1,120 | 510 | 2.20x |
| Workload | Alice, 32 cores | C (OpenMP), 32 cores | Bend, 16 cores (Apple M4 Max, other machine) |
|---|---|---|---|
| bfs | 12.9x | 21.4x | 11.4x |
| editdist | 15.6x | 17.3x | 10.8x |
| gameoflife | 12.7x | 24.8x | 12.1x |
| hashmap | 9.0x | 16.4x | 11.5x |
| kmeans | 11.9x | 10.4x | 7.7x |
| lexer | 10.4x | 9.2x | 10.8x |
| mandelbrot | 10.6x | 13.5x | 11.3x |
| merkle | 10.7x | 13.5x | 10.0x |
| Workload | Alice / C, serial | Bend / C, serial (M4) | Alice parallel / C serial | Bend parallel / C serial (M4) |
|---|---|---|---|---|
| bfs | 1.68 | 1.09 | 0.13 | 0.096 |
| editdist | 1.29 | 1.28 | 0.083 | 0.118 |
| gameoflife | 0.90 | 1.15 | 0.071 | 0.095 |
| hashmap | 1.03 | 4.41 | 0.114 | 0.383 |
| kmeans | 11.71 | 1.00 | 0.987 | 0.13 |
| lexer | 1.39 | 2.07 | 0.133 | 0.191 |
| mandelbrot | 1.51 | 1.19 | 0.142 | 0.105 |
| merkle | 1.74 | 1.23 | 0.163 | 0.123 |
Notes
- Bend was not run on this machine. Bend numbers are Bend's own pinned results from an Apple M4 Max (16 cores); compare them only through the ratios to C, never as milliseconds.
- Alice's startup floor (bfs with the smallest input) is 26 ms serial and 43 ms on 32 cores; it is part of every cold-process time.
- kmeans is Alice's largest gap to C. The fix (arena-backed sum types) is tracked in the roadmap.
Alice vs C# on the same .NET runtime
Eight kernels, each with an idiomatic C# twin and an expert hand-optimized C# twin: same algorithm, same checksum.
Machine
12th Gen Intel Core i5-1235U laptop (12 threads, turbo and thermal limits), Linux 7.0.9, .NET 10.0.4. Run-to-run spread is 10-20%, so read ratios within 1.2x as equal.
Compiler: Alice 0.12.0 (run 8, 2026-10-22)
Method
- Each kernel warms up for at least 50 calls and 1 s so tier-1 and PGO code is running, then 15 timed calls; the median is reported.
- Each timed call checks its checksum, so the JIT cannot drop the work. All checksums match.
- One process per benchmark variant. Both sides use server GC.
Source: benchmarks/README.md (run 8)
| Workload | Alice | C# idiomatic | C# hand-optimized | Alice / C# idiomatic |
|---|---|---|---|---|
| nbody | 47.8 | 53.3 | 48.9 | 0.90x |
| binarytrees | 171 | 168 | 91.4 | 1.02x |
| pipeline | 2 | 37.7 | 2.1 | 0.05x |
| expr | 10.3 | 11 | 10.5 | 0.94x |
| wordfreq | 6.9 | 80.5 | 5.3 | 0.09x |
| mandelbrot | 9.7 | 105 | 8.7 | 0.09x |
| parallel | 34.8 | 40.4 | 34 | 0.86x |
| particles | 2.4 | 136 | 2.4 | 0.02x |
Notes
- pipeline: Alice fuses map.select.sum into one vectorized loop split across cores; LINQ pays for delegates and iterators.
- Hand-optimized C# still leads on binarytrees (1.87x) and wordfreq (1.30x).
Alice vs C
Five of the .NET suite kernels written as idiomatic C, serial and with pthreads.
Machine
12th Gen Intel Core i5-1235U laptop (12 threads), Linux, clang 22 -O3 -march=native.
Compiler: Alice development build (runs 6 and 7, 2026-10-16)
Method
- Alice: warm kernel, median of 15, as in the .NET suite.
- C: c/bench.c, serial and pthreads-parallel. Same checksums as Alice and C#.
| Workload | Alice | C serial | C parallel (pthreads) |
|---|---|---|---|
| binarytrees | 90.1 | 301 | 321 |
| pipeline | 2.5 | 15.3 | 4 |
| mandelbrot | 9.8 | 102 | 15.3 |
| particles | 2.4 | 22.4 | 2.9 |
| nbody* | 47.2 | 29.9 | – |
- * nbody: With @[Native] (run 7), Alice measured 31.8-32.9 ms against C's 33.4-34.5 ms in the same session.
Notes
- nbody without @[Native] is a .NET code-generation limit: expert hand-optimized C# is also about 50 ms.
Fork/join on one machine: Bend, Alice, C, Rust, C#
Six fork/join workloads, same algorithm and the same binary fork shape in every language. All five languages ran on the same laptop.
Machine
12th Gen Intel Core i5-1235U laptop (2 performance + 8 efficiency cores, 12 threads), CachyOS Linux 7.0.9, .NET 10.0.4.
Compiler: Alice 0.13.x development build, Bend 2.0.39
Method
- Cold-process wall time (hyperfine -N, median of 5 after one warm-up). .NET pays about 22-27 ms of startup and JIT; Bend, C and Rust about 1 ms.
- Serial is pinned to one core. Parallel uses 12 threads: Bend forks every parallel let, Alice forks pure recursive pairs itself, C uses OpenMP tasks, Rust rayon::join, C# Parallel.Invoke.
Source: benchmarks/bend-vs/README.md
| Workload | Bend | Alice | C | Rust | C# |
|---|---|---|---|---|---|
| pow2* | 210 | 193 | 31 | 158 | 247 |
| fib | 43 | 140 | 27 | 42 | 94 |
| tree | 90 | 681 | 162 | 565 | 1,019 |
| nqueens | 144 | 282 | 78 | 111 | 187 |
| mandel | 100 | 152 | 121 | 92 | 218 |
| collatz | 159 | 356 | 186 | 83 | 326 |
- * pow2: clang turns pow2 into a closed form; the C number measures nothing.
| Workload | Bend | Alice | C | Rust | C# |
|---|---|---|---|---|---|
| pow2* | 1,541 | 1,218 | 1 | 1,206 | 1,414 |
| fib | 280 | 625 | 146 | 245 | 381 |
| tree | 123 | 1,542 | 472 | 774 | 1,430 |
| nqueens | 331 | 620 | 240 | 357 | 430 |
| mandel | 672 | 749 | 673 | 672 | 1,117 |
| collatz | 1,159 | 2,787 | 653 | 695 | 2,537 |
- * pow2: clang turns pow2 into a closed form; the C number measures nothing.
Notes
- Bend wins allocation-heavy trees by a wide margin: its nodes have no headers and are freed as they are read. Struct- and arena-backed trees are Alice's planned answer.
- Branchy integer loops (collatz) favour native compilers; Alice and C# land at the same time there, so the gap is in .NET's code generation, not in Alice's compiler.
Run them yourself
Each suite has a run.sh that builds the compiler and every program, then measures. See benchmarks/ in the repository. Please report results from your machine as an issue, with the machine and the command you ran.