Measured · 21–23 August 2026 · Türkçe
Same API. Ten languages. Three sizes. Thirty builds. All pass all tests. Then count tokens. Then measure speed, CPU, RAM.
Two big things. Token gap shrink when app grow. And token order is almost exact reverse of speed order.
Small = plain CRUD, 7 routes. Mid = add auth, roles, row access, middleware, logging, error codes, paging, sorting, state machine, cascade delete. 16 routes. Large = add module split, ETag concurrency, idempotency keys, soft delete, audit log, outbox, quota, bulk write, search, reports, metrics, comments, user admin. 34 routes.
Spec written first. Test written second. Code written last. Nothing count until real server run and pass every test over real HTTP. Mid and large also must pass log check that read server stdout and check every log line.
Bar = cost versus cheapest language that tier. Three bars each: small, mid, large. Expensive language get much cheaper when app get big.
C and C++ and Zig write HTTP parser, JSON parser, JSON writer, socket loop — one time. Python and C# import all that. So Python start way ahead, then gain nothing more. Every language pay same for route number 35.
Gap worst-to-best: 5.05× at 7 routes → 3.43× at 16 → 2.80× at 34.
So: measure on toy app, you punish C too hard — by near double.
Click any header to sort.
| Language | small | mid | large | large tokens | large lines |
|---|---|---|---|---|---|
| Python | 1.00× | 1.00× | 1.00× | 10,558 | 1,321 |
| TypeScript | 1.28× | 1.13× | 1.12× | 11,874 | 1,493 |
| C# | 1.12× | 1.08× | 1.15× | 12,180 | 1,360 |
| Java | 1.51× | 1.35× | 1.38× | 14,521 | 1,601 |
| PHP | 1.94× | 1.45× | 1.48× | 15,607 | 1,771 |
| Go | 1.75× | 1.75× | 1.56× | 16,518 | 2,338 |
| Rust | 1.66× | 1.72× | 1.66× | 17,560 | 2,147 |
| Zig | 2.08× | 1.87× | 1.84× | 19,418 | 1,995 |
| C++ | 4.24× | 2.50× | 2.01× | 21,254 | 2,467 |
| C | 5.05× | 3.43× | 2.80× | 29,532 | 3,245 |
Big app. 10,000 requests, one at a time. p50 = middle request. Use p50, not p99 — Windows timer tick make p99 lie. Cold start have harness floor removed.
Click any header to sort. Rank on p50 — req/s is noisy for sub-millisecond
build. Detail in METHODOLOGY.md.
| Language | p50 ms | req/s | CPU ms/req | idle RAM | cold start |
|---|---|---|---|---|---|
| C++ | 0.218 | 2,543 | 0.070 | 5.2 MB | 58 ms |
| C | 0.240 | 2,084 | 0.059 | 4.2 MB | 47 ms |
| Zig | 0.254 | 1,986 | 0.069 | 3.3 MB | 57 ms |
| Go | 0.311 | 1,888 | 0.230 | 8.6 MB | 55 ms |
| Rust | 0.327 | 1,412 | 0.150 | 6.2 MB | 69 ms |
| TypeScript | 0.441 | 1,198 | 0.356 | 67.0 MB | 452 ms |
| C# | 0.444 | 850 | 0.653 | 50.9 MB | 712 ms |
| Java | 0.595 | 760 | 1.613 | 191.0 MB | 5,275 ms |
| Python | 1.621 | 266 | 1.764 | 50.0 MB | 1,012 ms |
| PHP | 8.636 | 62 | 5.097 | 34.5 MB | 166 ms |
C and C++ cost most tokens — and win speed. Python cost fewest tokens — and place 9 of 10 on speed. Two lists near mirror image.
So no single best language here. Only trade you pick on purpose. Nothing sit good on both end.
Click any header to sort.
| Language | token rank | speed rank | shift | Caveman say |
|---|---|---|---|---|
| C++ | 9 | 1 | +8 | fastest, most token but one |
| C | 10 | 2 | +8 | least CPU, fastest start, most token |
| Zig | 8 | 3 | +5 | least RAM, 3.3 MB |
| Go | 6 | 4 | +2 | good all round, many lines |
| Rust | 7 | 5 | +2 | fast, safe, many token |
| TypeScript | 2 | 6 | −4 | best mix, eat 67 MB |
| C# | 3 | 7 | −4 | dense code, mid speed |
| Java | 4 | 8 | −4 | JIT help later, 5 s start, 191 MB |
| PHP | 5 | 10 | −5 | no dependency, but file store slow it |
| Python | 1 | 9 | −8 | fewest token, slow |
Shift = token rank minus speed rank. Plus mean faster than token cost suggest.
PHP had OPcache off. php -S run CLI SAPI, where
opcache.enable_cli default Off. So PHP recompile the whole app from source
every request. No real deploy do that. Fixing it near halve PHP p50 on mid tier.
C and C++ had no keep-alive. Both send Connection: close
every response, so new TCP handshake per request. HTTP/1.1 default is persistent. Zig —
also raw socket, no framework — do it right, which prove it our bug. After fix, C small
tier go 96 → 3,454 req/s, p50 11.59 → 0.166 ms.
Before fix we ranked C 8th on speed. C is actually near top.
Correct keep-alive cost C and C++ about +26% token. Real trade: protocol-correct code is more code. Number here are the corrected ones.
After OPcache fix we measured layer by layer, same server, fresh store, GET only. Answer surprise us.
| Layer | p50 | this layer add |
|---|---|---|
| bare PHP, no framework | 0.874 ms | — |
| + Slim boot, no store, no logic | 5.113 ms | +4.24 ms |
| + our small store and routes | 5.384 ms | +0.27 ms |
| + our large store and routes | 6.410 ms | +1.30 ms |
Bare PHP 0.874 ms — beat C# and Python. About 94% of gap is Slim container, router, middleware rebuilt from scratch every request. That is shared-nothing model, what PHP require. Not PHP language slow. Not our store slow. Every other language build router one time at startup and keep it for life of process.
In Go or Rust, framework is the floor — cannot serve HTTP with less. In PHP,
framework is a choice. PHP is request handler by design; one front controller on
$_SERVER['REQUEST_URI'] is normal PHP, used in production. Measuring Slim
charge PHP for a decision many PHP dev never make.
So PHP here is zero dependency — no composer, no vendor, no autoloader. Just
public/index.php and its own file. Same store, same names, same logic. Pass
same 22 / 98 / 139 test.
We measured both way before we dropped the framework one, so framework cost is a measured number, not a guess:
| 10,000 request | no framework | with Slim | gain |
|---|---|---|---|
| small tier p50 | 1.362 ms | 6.102 ms | 4.5× |
| mid tier p50 | 1.452 ms | 6.306 ms | 4.3× |
| large tier p50 | 8.636 ms | 17.588 ms | 2.0× |
| large CPU ms/req | 5.097 | 9.355 | 1.8× |
| large token | 15,607 | 16,238 | 1.04× |
No framework is 4.5× faster at small, 4.3× at mid,
2.0× at large, 5× less CPU — and cost fewer token too. Plus zero
manifest, because no composer file. So the framework cost about 4.2 ms every request and save
nothing we measured. Old Slim run kept in
perf-variance/removed-slim-run*.json so anyone can check.
These PHP number are median of three run. They hardly needed it: three run gave 1.362, 1.361, 1.376 ms at small — inside 1.1%. PHP is one of the most reproducible build in the whole benchmark. The old framework build swung 13.73, 6.07, 6.10 ms, because its autoloader touch about a hundred file per request. So the wobble was the framework, never the language.
Even with no framework, PHP looked bad at large tier. We measured why, and it was our fault again.
| Store operation | small store 501 B | big store 144 KB |
|---|---|---|
| file_get_contents | 0.049 ms | 0.039 ms |
| file_put_contents | 0.327 ms | 0.741 ms |
| json_decode | 0.016 ms | 8.581 ms |
| full read + decode + encode + write | 0.641 ms | 11.519 ms |
File I/O was never the problem. JSON was. Large tier grow an audit log and outbox, and our store re-serialize the whole dataset every request. So PHP pay O(total state) per request for what every other language do as O(1) append to a list in RAM. Our design, not PHP.
Fix: audit and outbox move to append-only JSON Lines file, counters in
store.json so /stats and /metrics stay O(1), and a
watermark so flush stay O(1) and delivered is derived not rewritten.
| 2,000 status change on one task | p50 start | p50 end | drift |
|---|---|---|---|
| before fix | 7.783 ms | 18.402 ms | 2.36× |
| after fix | 7.523 ms | 7.547 ms | 1.00× |
Flat. Large tier p50 drop 25.2 → 8.6 ms. Cost of the fix: +1,216 token, because O(1) persistence is more code than O(n). That move PHP from 4th to 5th on large-tier token. Honest trade, and the number here include it.
OPcache off. Framework rebuilt per request. Store O(n). Two of three were invisible until we measured layer by layer. Lesson: PHP shared-nothing model fight any benchmark that assume state live in memory, and a naive port quietly bill PHP for it. Read any PHP benchmark — this one too — with that in mind.
After the store fix, large tier PHP go 2.36 ms at 100 request → 8.64 ms at 10,000. Remaining growth is 1,000 soft-deleted task row sitting in the store as live domain state — which every language in the tier also hold. Legitimate, so it stay.
No framework is faster and smaller: 4.5× faster p50 at small, 4.3×
at mid, 2.0× at large, 5× less CPU, and 15,607 token vs 16,238. The framework
also add a composer.json and a vendor/ tree. Nothing measured
here got better for it, so PHP is measured with none.
Java get faster while run: p50 1.304 → 0.595 ms. Only big improver, drift 0.46×. But pay 5,275 ms cold start and 191 MB RAM. Fine for long server. Bad for scale-to-zero.
0.059 CPU ms per request on big app. That is 86× less than PHP, 27× less than Java. And fastest cold start, 47 ms.
3.3 MB idle. Java use 191 MB — 57× more. Zig do it on standard library only, zero dependency, all three tiers.
C# beat Python on line count at small and mid — 134 vs 138, 451 vs 472. Modern C# not old enterprise boilerplate.
Token spread 2.80×. Cold start 112×. CPU 86×. RAM 57×. Speed 40×. Source size flattest thing measured here.
409. Real limit, and it make
C latency there look slightly better than true.