Skip to content

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:

bash
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.sh

CPU: Fibonacci(35)

MetricTime
fib(35), --release binary41.6ms
Rust 1.96 (rustc -O)44.6ms
C (clang -O2)46.1ms
Go 1.2648.2ms
Python 3.14952ms

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.

MetricWynGo 1.26Python 3.14
sort 100K ints6.3ms7.0ms21.5ms
sort 1M ints73ms83ms291ms

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

MetricValue
Spawn overhead~1.5 μs per spawn+await
4x fib(35) via spawn + await_all33ms (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 spawn0.69s (Go goroutines: 0.28s)
SchedulerM: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.

CommandTime
wyn check12ms
wyn build (dev)356ms
wyn build --release1.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

OperationWynPython 3.14
1M StringBuilder appends5.6ms36ms (list + join)
1M naive s = s + "x"11.5s9.9s
1M .len()1.7ms99ms
100K .upper().trim() chains9.2ms15ms

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

  1. Wyn's runtime is much younger than Go's or Python's - treat single-benchmark wins with suspicion
  2. CPU-bound code performance depends largely on the C compiler, not Wyn
  3. 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
  4. Naive string concatenation is a genuine weak spot (see above)
  5. 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.

MIT License - v1.21.0