Benchmarks

Faster than Rust and Zig. Far ahead of Go and Bun.

Every chart is a measured result from the reports that ship with the compiler: same machine, interleaved runs, outputs checked on every run. Those reports also list where Neo is behind.

HTTP throughput 1 worker, hello world req/s, higher is better · x86-64 Linux
Neo 111k
Rust 80.4k
Bun 76.4k
Zig 75.9k
Go 54.2k

1.38× more than Rust1.45× more than Bun1.46× more than Zig2.04× more than Go

x86-64 Linux, all five languages · 2 November 2026 · method

Against Rust

Optimised Rust with serde and hyper, on x86-64 Linux.

Compute

N-body 4,096 bodies × 10 steps ms, lower is better · x86-64 Linux
Neo 345 ms
Rust 633 ms

1.83× faster than Rust

N-body 1,024 bodies × 3 steps ms, lower is better · x86-64 Linux
Neo 10.0 ms
Rust 16.2 ms

1.62× faster than Rust

Mandelbrot 1200 × 1200, 300 iterations ms, lower is better · x86-64 Linux
Neo 132 ms
Rust 186 ms

1.41× faster than Rust

Stencil 1M cells × 20 passes ms, lower is better · x86-64 Linux
Neo 33.6 ms
Rust 46.5 ms

1.38× faster than Rust

JSON

Dynamic JSON 1,000-order array × 1,000, vs serde_json::Value ms, lower is better · x86-64 Linux
Neo 175 ms
Rust 350 ms

2.00× faster than Rust

Dynamic JSON small document × 1M, vs serde_json::Value ms, lower is better · x86-64 Linux
Neo 176 ms
Rust 332 ms

1.89× faster than Rust

Typed decode small document × 1M, vs serde derive ms, lower is better · x86-64 Linux
Neo 122 ms
Rust 169 ms

1.39× faster than Rust

Typed decode 1,000-order array × 1,000, vs serde derive ms, lower is better · x86-64 Linux
Neo 87.5 ms
Rust 96.5 ms

1.10× faster than Rust

HTTP

HTTP throughput 2 workers, keep-alive, 64 connections req/s, higher is better · x86-64 Linux
Neo 214k
Rust 143k

1.50× more than Rust

HTTP throughput 1 worker, keep-alive, 64 connections req/s, higher is better · x86-64 Linux
Neo 119k
Rust 88.8k

1.34× more than Rust

HTTP p99 latency 2 workers, same load µs, lower is better · x86-64 Linux
Neo 379 µs
Rust 713 µs

1.88× lower than Rust

Server CPU per request 1 worker, user + kernel µs, lower is better · x86-64 Linux
Neo 8.39 µs
Rust 11.32 µs

1.35× lower than Rust

x86-64 Linux · 30–31 October 2026 · method

HTTP in depth

One executable serves with either event loop, so epoll and io_uring are compared with identical request handling. CPU per request is user plus kernel time of every server process divided by completed requests.

Throughput by worker count Keep-alive hello world, 64 connections, wrk on four separate CPUs req/s, higher is better · x86-64 Linux
1 worker
Rust 89k
Neo epoll 98k
Neo io_uring 119k
2 workers
Rust 143k
Neo epoll 192k
Neo io_uring 214k
4 workers
Rust 268k
Neo epoll 319k
Neo io_uring 310k

With four workers the load generator saturates first (servers 78–90% busy), so throughput flattens while CPU per request keeps falling.

Where each request's CPU time goes Server CPU per request, split into the server's own code and the kernel µs, lower is better · x86-64 Linux
Server's own codeKernel (TCP, sockets, scheduling)
1 worker
Rust 1.52 + 9.80
Neo epoll 0.76 + 9.39
Neo io_uring 0.19 + 8.20
2 workers
Rust 2.74 + 11.06
Neo epoll 0.77 + 9.63
Neo io_uring 0.20 + 9.14
4 workers
Rust 3.15 + 10.34
Neo epoll 1.04 + 9.81
Neo io_uring 0.62 + 9.51

Neo's own code costs 0.19 µs per request against Rust's 1.52 µs. The rest is kernel work every server pays, which is why HTTP is 1.3–1.5× rather than 2×.

Tail latency (p99) The slowest 1% of requests under the same load µs, lower is better · x86-64 Linux
1 worker
Rust 970 µs
Neo epoll 870 µs
Neo io_uring 739 µs
2 workers
Rust 713 µs
Neo epoll 649 µs
Neo io_uring 379 µs
4 workers
Rust 439 µs
Neo epoll 247 µs
Neo io_uring 245 µs

Against Go, Bun and Rust

The same servers and JSON workload, with Go and Bun counterparts, on ARM64 Linux.

HTTP throughput 1 worker, hello world req/s, higher is better · ARM64 Linux
Neo 262k
Rust 197k
Bun 113k
Go 87.4k

1.33× more than Rust2.31× more than Bun2.99× more than Go

HTTP throughput 4 workers, hello world req/s, higher is better · ARM64 Linux
Neo 562k
Rust 486k
Go 234k
Bun 119k

1.16× more than Rust2.40× more than Go4.72× more than Bun

Pipelined HTTP 1 worker req/s, higher is better · ARM64 Linux
Neo 2.02M
Rust 726k
Go 112k
Bun 24.6k

2.79× more than Rust18.0× more than Go82.3× more than Bun

Dynamic JSON 1M documents: build, parse, read, checksum ms, lower is better · ARM64 Linux
Neo 229 ms
Bun 404 ms
Rust 484 ms
Go 1,711 ms

1.76× faster than Bun2.11× faster than Rust7.46× faster than Go

Server memory peak PSS under load, 1 worker MiB, lower is better · ARM64 Linux
Neo 0.79 MiB
Rust 2.43 MiB
Go 12.1 MiB
Bun 53.3 MiB

3.08× less than Rust15.3× less than Go67.6× less than Bun

Server CPU per request 1 worker, user + kernel µs, lower is better · ARM64 Linux
Neo 3.79 µs
Rust 5.05 µs
Bun 8.80 µs
Go 11.37 µs

1.34× lower than Rust2.33× lower than Bun3.00× lower than Go

ARM64 Linux · 27 October 2026 · method

Against Zig

Release-mode Zig with std.json, std.http and http.zig, on x86-64 Linux. Each row uses the fastest Zig build and configuration measured; each HTTP server gets one vCPU, with the load generator on another core.

Compute

N-body 4,096 bodies × 10 steps ms, lower is better · x86-64 Linux
Neo 377 ms
Zig 685 ms

1.82× faster than Zig

N-body 1,024 bodies × 3 steps ms, lower is better · x86-64 Linux
Neo 11.8 ms
Zig 17.8 ms

1.51× faster than Zig

Mandelbrot 1200 × 1200, 300 iterations ms, lower is better · x86-64 Linux
Neo 146 ms
Zig 203 ms

1.40× faster than Zig

Stencil 50M cells × 20 passes ms, lower is better · x86-64 Linux
Neo 1,421 ms
Zig 1,777 ms

1.25× faster than Zig

Stencil 1M cells × 20 passes ms, lower is better · x86-64 Linux
Neo 30.7 ms
Zig 37.6 ms

1.22× faster than Zig

Sieve primes below 100M ms, lower is better · x86-64 Linux
Neo 439 ms
Zig 502 ms

1.14× faster than Zig

JSON

Dynamic JSON small document × 1M, vs Zig's fastest std.json variant (typed, arena) ms, lower is better · x86-64 Linux
Neo 188 ms
Zig 334 ms

1.77× faster than Zig

Dynamic JSON 1,000-order array × 1,000, vs Zig's fastest std.json variant (typed, arena) ms, lower is better · x86-64 Linux
Neo 187 ms
Zig 218 ms

1.17× faster than Zig

HTTP

HTTP throughput hello world, one vCPU, 64 connections req/s, higher is better · x86-64 Linux
Neo 109k
http.zig 74.4k
std.http 58.4k

1.46× more than http.zig1.86× more than std.http

Rendered page about 5 KB of HTML built per request req/s, higher is better · x86-64 Linux
Neo 68.5k
http.zig 54.0k
std.http 45.6k

1.27× more than http.zig1.50× more than std.http

Compute per request N-body, 256 bodies × 4 steps per POST req/s, higher is better · x86-64 Linux
Neo 1.3k
std.http 0.8k
http.zig 0.8k

1.67× more than std.http1.69× more than http.zig

p99 latency N-body per POST, same load µs, lower is better · x86-64 Linux
Neo 49,810 µs
http.zig 113,190 µs
std.http 349,280 µs

2.27× lower than http.zig7.01× lower than std.http

CPU-bound route 32 smoothing passes over 10,000 values req/s, higher is better · x86-64 Linux
Neo 2.9k
std.http 2.4k
http.zig 2.4k

1.21× more than std.http1.23× more than http.zig

Pipelined HTTP 16 requests per write (http.zig answers only the first, so it is not run) req/s, higher is better · x86-64 Linux
Neo 1.43M
std.http 101k

14.1× more than std.http

Server CPU per request hello world, user + kernel µs, lower is better · x86-64 Linux
Neo 9.19 µs
http.zig 13.39 µs
std.http 17.11 µs

1.46× lower than http.zig1.86× lower than std.http

Server memory peak RSS under hello-world load MiB, lower is better · x86-64 Linux
Neo 0.80 MiB
http.zig 3.73 MiB
std.http 18.0 MiB

4.69× less than http.zig22.6× less than std.http

x86-64 Linux, against Zig · 2 November 2026 · method

Raw numbers

Against Rust · x86-64 Linux

WorkloadNeoRustNeo lead
N-body4,096 bodies × 10 steps · ms, lower is better 345.3 633.0 1.83×
N-body1,024 bodies × 3 steps · ms, lower is better 10.0 16.2 1.62×
Mandelbrot1200 × 1200, 300 iterations · ms, lower is better 131.8 185.9 1.41×
Stencil1M cells × 20 passes · ms, lower is better 33.6 46.5 1.38×
WorkloadNeoRustNeo lead
Dynamic JSON1,000-order array × 1,000, vs serde_json::Value · ms, lower is better 175.2 349.9 2.00×
Dynamic JSONsmall document × 1M, vs serde_json::Value · ms, lower is better 175.5 331.5 1.89×
Typed decodesmall document × 1M, vs serde derive · ms, lower is better 122.1 169.2 1.39×
Typed decode1,000-order array × 1,000, vs serde derive · ms, lower is better 87.5 96.5 1.10×
WorkloadNeoRustNeo lead
HTTP throughput2 workers, keep-alive, 64 connections · req/s 213,863 142,923 1.50×
HTTP throughput1 worker, keep-alive, 64 connections · req/s 118,817 88,802 1.34×
HTTP p99 latency2 workers, same load · µs, lower is better 379 713 1.88×
Server CPU per request1 worker, user + kernel · µs, lower is better 8.39 11.32 1.35×

HTTP · x86-64 Linux

WorkersServerreq/sp50 µsp99 µsCPU/request µs
1Rust (hyper)88,80271397011.32
1Neo, epoll98,05163887010.15
1Neo, io_uring118,8175167398.39
2Rust (hyper)142,92344771313.79
2Neo, epoll191,90830564910.40
2Neo, io_uring213,8632883799.34
4Rust (hyper)268,25622043913.49
4Neo, epoll318,56310024710.85
4Neo, io_uring309,89510324510.13

Against Go, Bun and Rust · ARM64 Linux

WorkloadNeoRustGoBunNeo lead
HTTP throughput1 worker, hello world · req/s 261,509 196,81487,434113,025 1.33×–2.99×
HTTP throughput4 workers, hello world · req/s 561,581 485,714233,903118,982 1.16×–4.72×
Pipelined HTTP1 worker · req/s 2,023,817 726,309112,26324,597 2.79×–82.3×
Dynamic JSON1M documents: build, parse, read, checksum · ms, lower is better 229.3 484.31,711404.3 1.76×–7.46×
Server memorypeak PSS under load, 1 worker · MiB, lower is better 0.79 2.4312.153.3 3.08×–67.6×
Server CPU per request1 worker, user + kernel · µs, lower is better 3.79 5.0511.378.80 1.34×–3.00×

Against Zig · x86-64 Linux

WorkloadNeoZighttp.zigstd.httpNeo lead
N-body4,096 bodies × 10 steps · ms, lower is better 377.1 684.7—— 1.82×
N-body1,024 bodies × 3 steps · ms, lower is better 11.8 17.8—— 1.51×
Mandelbrot1200 × 1200, 300 iterations · ms, lower is better 145.5 203.4—— 1.40×
Stencil50M cells × 20 passes · ms, lower is better 1,421 1,777—— 1.25×
Stencil1M cells × 20 passes · ms, lower is better 30.7 37.6—— 1.22×
Sieveprimes below 100M · ms, lower is better 439.0 502.3—— 1.14×
WorkloadNeoZighttp.zigstd.httpNeo lead
Dynamic JSONsmall document × 1M, vs Zig's fastest std.json variant (typed, arena) · ms, lower is better 188.1 333.7—— 1.77×
Dynamic JSON1,000-order array × 1,000, vs Zig's fastest std.json variant (typed, arena) · ms, lower is better 186.6 218.3—— 1.17×
WorkloadNeoZighttp.zigstd.httpNeo lead
HTTP throughputhello world, one vCPU, 64 connections · req/s 108,829 —74,41458,427 1.46×–1.86×
Rendered pageabout 5 KB of HTML built per request · req/s 68,508 —53,95245,553 1.27×–1.50×
Compute per requestN-body, 256 bodies × 4 steps per POST · req/s 1,329 —786796 1.67×–1.69×
p99 latencyN-body per POST, same load · µs, lower is better 49,810 —113,190349,280 2.27×–7.01×
CPU-bound route32 smoothing passes over 10,000 values · req/s 2,880 —2,3512,381 1.21×–1.23×
Pipelined HTTP16 requests per write (http.zig answers only the first, so it is not run) · req/s 1,426,310 ——100,985 14.1×
Server CPU per requesthello world, user + kernel · µs, lower is better 9.19 —13.3917.11 1.46×–1.86×
Server memorypeak RSS under hello-world load · MiB, lower is better 0.80 —3.7318.0 4.69×–22.6×

Method

x86-64 Linux · 30–31 October 2026

Machine
Temporary Vultr instances with dedicated AMD EPYC vCPUs: 2 vCPUs for compute and JSON (one pinned CPU per worker), 8 vCPUs for HTTP (4 for the server, 4 for the load generator).
Software
Ubuntu 24.04, Linux 6.8, clang 18.1.3, rustc 1.99.1 (release builds), hyper/tokio with pipeline_flush, wrk -t4 -c64.
Procedure
Interleaved runs on the same machine; medians of 7–30 rounds (compute), 11–15 (JSON) and 5 × 10 s after a 2 s warm-up (HTTP). Outputs are checked against each other on every run.
Reports
RESULTS-compute-db-x86-linux-2026-10-30.md
RESULTS-http-uring-x86-linux-2026-10-31.md

ARM64 Linux · 27 October 2026

Machine
Ten-vCPU Linux VM on an Apple M1 Pro. One-worker cases on CPU 0; load generators on separate CPUs.
Software
Linux ARM64, clang 18.1.3, rustc 1.98.1, Go 1.25.14, Bun 1.3.13.
Procedure
Nine shuffled measured rounds after two warm-ups; five paired HTTP trials per scenario with a validating client that checks every response body.
Reports
RESULTS-core-regression-linux-2026-10-27.md

x86-64 Linux, against Zig · 2 November 2026

Machine
Temporary Vultr instances with dedicated AMD EPYC-Milan vCPUs. Compute and JSON: 2 vCPUs, one pinned CPU per worker. HTTP: 4 vCPUs (2 cores); every server is confined to one vCPU with its SMT sibling idle, and wrk runs on the other core, so the server is the bottleneck.
Software
Ubuntu 24.04, Linux 6.8, Zig 0.17.0 ReleaseFast (baseline and -mcpu=native builds; the faster one is shown), std.json, std.http and http.zig (the better of its default and 1+1 configurations on each row), wrk -t2 -c64.
Procedure
Interleaved runs on the same machine; medians of 7 rounds (compute), 9 (JSON) and 11 × 10 s after a 2 s warm-up (HTTP). Outputs and responses are checked on every run. Transparent huge pages at Ubuntu's default (madvise); the 50M-cell stencil and sieve margins depend on them.
Reports
RESULTS-zig-x86-linux-2026-11-02.md

x86-64 Linux, all five languages · 2 November 2026

Machine
One temporary Vultr instance with 4 dedicated AMD EPYC-Milan vCPUs (2 cores). Each HTTP server is confined to one vCPU with its SMT sibling idle and wrk runs on the other core, so the server is the bottleneck. JSON and N-body run pinned to one vCPU with every runtime on one thread.
Software
Ubuntu 24.04, Linux 6.8, clang 18.1.3, rustc 1.99.1 (hyper/tokio, serde_json), Go 1.25.14, Bun 1.4.4, Zig 0.17.0 ReleaseFast, wrk -t2 -c64. Each Zig bar is its fastest variant on that row: http.zig for throughput (default) and memory (1+1), std.http for pipelining (http.zig answers only the first request of a pipelined write), and std.json parsing into typed structs with an arena.
Procedure
All five languages in the same interleaved rounds on one machine; medians of 11 × 10 s HTTP runs after a 2 s warm-up with a fresh server per run, and of 15 rounds after 2 warm-ups for JSON and N-body. Every server passes a hello and a 16-deep pipelining check first; JSON checksums and N-body energies match on every run. Memory is the server's summed PSS, sampled every 100 ms.
Reports
RESULTS-headline-x86-linux-2026-11-02.md

These are comparisons of specific implementations on specific workloads, not rankings of languages. Competitor programs are idiomatic release builds; every run validates outputs, and the temporary machines are destroyed afterwards.