Wyn Benchmarks
March 2026 - Updated with honest numbers. Re-measured end to end on 26 August 2026 for v1.21.0; the corrections are called out where they land.
The canonical, always-current tables live in the benchmarks documentation, including the full method. This post is the short version.
All benchmarks run on Apple M3 Pro (6 performance + 6 efficiency cores, macOS 26, Wyn 1.21.0 release tarball), wyn build --release (clang -O2). Warm medians; process-level timings include a ~7ms process-startup floor. Run them yourself:
cd wyn
./wyn build tests/benchmarks/bench_suite.wyn && ./tests/benchmarks/bench_suite
./wyn build tests/benchmarks/spawn_benchmark.wyn && ./tests/benchmarks/spawn_benchmark
./benchmarks/http_load.shCPU: Fibonacci(35)
| Metric | Time |
|---|---|
fib(35), --release binary | 41.6ms |
Rust 1.96 (rustc -O) | 44.6ms |
C (clang -O2) | 46.1ms |
| Go 1.26 | 48.2ms |
| Python 3.14 | 952ms |
Wyn emits C and compiles with the system C compiler. CPU-bound performance is determined by the C compiler's optimizer, not Wyn. Being ahead of C on this one function is an inlining artifact, not a claim.
Sorting
The sort() call itself, not the whole process. Identical input in all three languages, verified by comparing the sorted output.
| Metric | Wyn | Go 1.26 | Python 3.14 |
|---|---|---|---|
| sort 100K ints | 6.3ms | 7.0ms | 21.5ms |
| sort 1M ints | 73ms | 83ms | 291ms |
Correction. An earlier edition of this post's companion pages had Go winning the 1M sort. Those Wyn figures were from a dev build compared against go build, which optimises by default. A Wyn dev build takes 110ms here; --release takes 73ms.
Concurrency
| Metric | Value |
|---|---|
| Spawn overhead | ~1.5 μs per spawn+await |
4x fib(35) via spawn + await_all | 33ms (same as one fib(35) - real 4x scaling) |
4x fib(35) in parallel { } | 62ms (two dispatch rounds - see below) |
| 10K spawn+await (sequential) | 14.9ms |
| 100K spawn+await (sequential) | 159ms |
1M fire-and-forget spawn | 0.69s (Go goroutines: 0.28s) |
| Scheduler | M:N coroutines across CPU-count worker threads |
Since v1.20, awaited spawn runs on the M:N coroutine scheduler by default, multiplexed across worker threads sized to your CPU count, giving true multi-core parallelism.
Correction. This post used to credit parallel { } with the 4x result. That number belongs to spawn + await_all. Measured back to back in one process on v1.21.0, parallel { } overlaps two CPU-bound branches fully (34ms) but takes two dispatch rounds for three or four (62-66ms), where four spawns plus await_all finish in 33ms. Use spawn + await_all when you need dependable overlap; the parallel { } width is filed as a defect.
Past four concurrent CPU-bound tasks you get throughput rather than latency: 4 tasks 34ms, 6 tasks 65ms, 8 tasks 99ms, 24 tasks 120ms.
Build Speed
Output deleted before each run, so every row is a real build.
| Command | Time |
|---|---|
wyn check | 12ms |
wyn build (dev) | 356ms |
wyn build --release | 1.18s |
wyn run (already compiled) | 28ms |
The dev figure moved from 288ms because that one was measured in a source checkout, which carries a precompiled header the release tarball deliberately does not ship. 356ms is what a fresh install does.
Strings
| Operation | Wyn | Python 3.14 |
|---|---|---|
1M StringBuilder appends | 5.6ms | 36ms (list + join) |
1M naive s = s + "x" | 11.5s | 9.9s |
1M .len() | 1.7ms | 99ms |
100K .upper().trim() chains | 9.2ms | 15ms |
Naive concatenation in a loop is quadratic and Wyn is slightly slower than Python at it. Use StringBuilder in hot loops - that is the number that matters, and it is ~2,000x faster.
.len() is O(1) via a length cache in the RC header - except on some StringBuilder.to_string() results, which arrive without one and fall back to an O(n) scan (~2.5μs per call on a 100,000-character string). Hoist .len() out of a loop if you are calling it on a builder result. Filed against the compiler.
Binary Size
Hello world is 50KB with --release (51,592 bytes) and 51KB dev (52,696 bytes), statically linked runtime included. A 92-line web REST API is 69KB. For scale on the same machine: C 33KB, Rust 421KB, Go 2.4MB.
Web
Two full sweeps of the committed ./benchmarks/http_load.sh harness, on a machine that was not idle (load 17 and 60 - the harness warns about this). They agree within ~5%: ~21,000-23,200 req/s with HTTP/1.1 keep-alive, ~6,500-7,100 req/s with a connection per request. Zero failed requests, and 9 open file descriptors after each sweep, so no leak.
Caveats
- Wyn's runtime is much younger than Go's or Python's - treat single-benchmark wins with suspicion
- CPU-bound code performance depends largely on the C compiler, not Wyn
- Concurrency overhead (~1.5μs/spawn+await) is adequate for real workloads but not competitive with Go (~1μs/goroutine), and Go is ~4x denser in memory at 1M outstanding tasks
- Naive string concatenation is a genuine weak spot (see above)
- Every "correction" above came from re-running a number rather than re-reading it. Numbers rot; that is the argument for a committed harness.
For current cross-language numbers, see the benchmarks documentation.
Related
- Spawn & Await - concurrency performance details
- Wyn vs Go - language comparison with benchmarks
- Wyn vs Python - performance comparison