Benchmark Sonuçları
Tulpar’ın sloganı “Python kadar kolay, C kadar hızlı”. CPU’da Rust/Go
bölgesinde; HTTP’de çok-çekirdekli listen_pool sunucusu Go’nun net/http’sini
geçiyor ve FastAPI’yi çok geride bırakıyor — hepsi gönderilecek bir runtime
olmadan, tek bağımsız binary’den.
CPU benchmark’ları
Section titled “CPU benchmark’ları”benchmarks/fib.tpr, native LLVM i64 codegen için typed AOT path (explicit
: int return) kullanır. Özyineleme derinliği bir env var’dan okunur, böylece
LLVM sonucu derleme zamanında kısmi-değerlendiremez.
| Ortam | C | Tulpar AOT | Oran |
|---|---|---|---|
Windows 11 / MinGW64, fib(35), best-of-3 | 83 ms | 114 ms | 1.37× C |
Linux WSL2 / gcc 15 -O2, fib(40), best-of-5 | 140 ms | 261 ms | 1.86× C |
Oran yalnızca Tulpar’a değil, C derleyicisine de bağlı. Her iki taraf da
LLVM/AOT-native; fark, gcc -O2’ye (çok agresif bir C) karşı MinGW’nin eski
GCC’sine göre daha geniş. Her iki durumda da Tulpar ~1.4–1.9× C bandında —
Rust ve Go ile aynı mahalle, yorumlanan Python’un kat kat önünde.
HTTP throughput
Section titled “HTTP throughput”benchmarks/loadtest (native C yük üreteci), her sunucuyu keep-alive bağlantılar
üzerinden GET / → JSON {"hello":"world"} ile döver; concurrency 1–12 arasında
taranır (kutunun çekirdek sayısının altında tutulur, böylece yük üreteci sunucuyu
aç bırakmaz), seviye başına 4 sn, en iyi koşu ikinci geçişle doğrulanır.
Kutu: 14-vCPU WSL2. Her runtime kendi önerilen tek-process yapılandırmasında.
| Sunucu | istek/sn | p50 latency | Yapılandırma |
|---|---|---|---|
Tulpar listen_pool | ~36k | 0.32 ms | 14 çekirdek, 1 process |
Go net/http | ~30k | 0.38 ms | tüm çekirdekler (default), 1 process |
Node.js http | ~8.7k | 1.06 ms | 1 thread (default) |
| FastAPI (uvicorn) | ~3.5k | 3.31 ms | 1 worker (default) |
Tulpar listen | ~4–4.7k | 0.22 ms | 1 thread, seri accept döngüsü |
Thread modelleri farklıdır ve yukarıda etiketlenmiştir: listen_pool ve Go’nun
net/http’si kutudan çıkarken tüm çekirdekleri kullanır, Node ve tek bir uvicorn
worker’ı ise default’ta tek. Tulpar’ın tek-thread listen()’i seri bir
accept döngüsüdür — en düşük request-başı latency’ye sahip (0.22 ms p50) ama
keep-alive bağlantıları serileştirir; bu yüzden throughput için listen_pool
(veya listen_async) kullan. Dikkat: tek-thread listen() burada tek-thread
Node’un gerisinde kalıyor; Tulpar’ın üstünlüğü listen_pool’un çekirdekler
arasında temiz ölçeklenmesinden geliyor (36k istek/sn’de p50 0.32 ms’de kalıyor,
temiz sub-millisecond tail ile).
Özellikle FastAPI’ye karşı Tulpar latency (~10× daha düşük p50) ve ayak izinde de net kazanıyor — ayrıntılı Wings vs FastAPI yazısına bak (yük altında p50 0.31 ms vs 28 ms, 6.7 MB vs 54 MB RSS, 2 MB bağımsız binary vs Python + ~50 MB bağımlılık).
Bizi buraya getirenler
Section titled “Bizi buraya getirenler”Uygulanan hot-path optimizasyonları:
call(handler_name)dlsym cache (256-slot FNV-1a hash) — request başı sembol-tablosu walk’unu eliyor.- Accept’te TCP_NODELAY — Nagle’ın 40ms gecikmesini kaldırır (+%13).
- Static thread-local recv buffer — keep-alive request başı 64KB malloc/free pair’ini düşürür.
- Per-request arena reset + per-request malloc region — uzun süreli sunucularda sızıntısız bounded memory.
- Built-in’lerde thread-local scratch buffer — TLS olmayan static’ler
listen_poolaltında yarışıyordu (birtoStringbuffer’ı düzeltilene dek ~%1.1 hatalı 404’e yol açtı). break/continuereal codegen — eskiden silently no-op’tu; LLVM induction-variable update’lerinde suboptimal phi node üretmesini engeller.
Bilinçli atladığımız trade-off’lar (analiz edildi, düşük değer):
- Object-key inline caching (
req["method"]HTTP path’in ~%0.3’ü). - String concat coalescing (
a + b + cHTTP path’in ~%0.1’i).
Metodoloji
Section titled “Metodoloji”- CPU:
gcc -O2(Linux gcc 15 / Windows MinGW64). Best-of-N wall-clock. İş yükü girdisi derleyiciye opaque’tır (runtime’da okunur), böylece-O2onu constant-fold edemez;Nprocess startup’ı önemsiz kılacak kadar büyüktür. - HTTP: native
benchmarks/loadtest, tek kutu, keep-alive, concurrency çekirdek sayısı ≤ tutulur, böylece yük üreteci ve sunucu CPU için yarışmaz. Toolchain’ler: Go 1.23, Node 22, Python 3.14 + FastAPI/uvicorn. Sunucular default tek-process yapılandırmasında çalışır (çekirdek kullanımı satır başına etiketli). - Mutlak rakamlar kutuya özeldir; taşınabilir sinyal olarak oranları ve
latency’yi baz al. CPU’yu
benchmarks/run_benchmarks.shile yeniden üret; buradaki HTTP sunucuları + sürücü, aynı JSON’u döndüren minimal eşdeğerlerdir.