İçeriğe geç

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.

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.

OrtamCTulpar AOTOran
Windows 11 / MinGW64, fib(35), best-of-383 ms114 ms1.37× C
Linux WSL2 / gcc 15 -O2, fib(40), best-of-5140 ms261 ms1.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.

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.

Sunucuistek/snp50 latencyYapılandırma
Tulpar listen_pool~36k0.32 ms14 çekirdek, 1 process
Go net/http~30k0.38 mstüm çekirdekler (default), 1 process
Node.js http~8.7k1.06 ms1 thread (default)
FastAPI (uvicorn)~3.5k3.31 ms1 worker (default)
Tulpar listen~4–4.7k0.22 ms1 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).

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_pool altında yarışıyordu (bir toString buffer’ı düzeltilene dek ~%1.1 hatalı 404’e yol açtı).
  • break / continue real 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 + c HTTP path’in ~%0.1’i).
  • 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 -O2 onu constant-fold edemez; N process 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.sh ile yeniden üret; buradaki HTTP sunucuları + sürücü, aynı JSON’u döndüren minimal eşdeğerlerdir.