
Triton Inference Server ile Fraud Tespitinde Gecikmeyi 300ms’den 25ms’ye Düşürmek: Dynamic Batching Vaka Çalışması

Giriş
Ödeme altyapılarında sahtekarlık (fraud) tespiti, milisaniyelerle yarışılan ve hata payı olmayan bir mühendislik problemidir. 3D Secure doğrulamasından önce, bir işlemin risk skorunu hesaplamak için sistemin sahip olduğu maksimum zaman bütçesi 100ms’dir. 2023’ün son çeyreğindeki kampanya döneminde, saniyede 4500 isteği (TPS) karşılamaya çalışan sistemimizde ciddi bir darboğazla karşılaştık. FastAPI ve PyTorch üzerine kurulu monolitik çıkarım (inference) servisimizin p99 gecikmesi 300ms bandına oturdu.
Bu gecikme artışı, %4.2 oranında SLA ihlaline (timeout) yol açtı. Timeout olan işlemler, risk politikamız gereği “soft-decline” (geçici ret) statüsüne düşerek yalnızca 4 saat içinde 2.8 milyon TL’lik onaylanabilir işlem kaybına neden oldu. Problemin kaynağını incelediğimizde sorunun donanım eksikliği değil, kaynak kullanımındaki mimari bir hata olduğunu gördük. AWS üzerindeki g4dn.xlarge (NVIDIA T4 GPU) sunucularımızda GPU kullanım oranı (utilization) %18’i geçemezken, PCIe veri yolu üzerinden CPU-GPU arası bağlam geçişleri (context switching) ve tekil (batch_size=1) tensor kopyalama işlemleri sistemi kilitliyordu.
Çözüm olarak donanımı yatayda ölçeklemek (horizontal scaling) yerine, C++ tabanlı NVIDIA Triton Inference Server’a geçiş yaparak Dynamic Batching mimarisini devreye aldık. Sonuç: Aynı donanım maliyetiyle p99 gecikmesi 300ms’den 25ms’ye düştü, tek nodun taşıdığı TPS kapasitesi 400’den 2800’e çıktı.
İçindekiler
- Mevcut Durum ve Darboğaz Analizi
- Triton Inference Server Mimarisi ve ONNX Dönüşümü
- Dynamic Batching Konfigürasyonu (Kod Örnekli)
- Trade-off Analizi: Hangi Aracı Seçmeli?
- Parametre Optimizasyonu ve Performans Testleri
- Pratik Öneriler / Production Notları
- Sık Sorulan Sorular
- Sonuç
Mevcut Durum ve Darboğaz Analizi
Python GIL ve FastAPI Kısıtlamaları
Eski mimarimizde, gelen her HTTP isteği Uvicorn worker’ları tarafından karşılanıyor, JSON payload’u PyTorch tensor’üne çevriliyor ve model.forward() çağrısı ile GPU’ya iletiliyordu. Bu tasarımda iki büyük temel sorun mevcuttu:
- Python GIL Kilitlenmeleri: Asenkron bir framework (FastAPI) kullansak da, PyTorch’un GPU’ya veri gönderdiği (Host-to-Device) anlarda C-level operasyonlar GIL’i bloke etmese bile, isteklerin kuyruklanması ve serileştirilmesi tek thread üzerinde ciddi CPU döngüsü tüketiyordu.
- I/O Sınırı (I/O Bound) vs İşlem Sınırı (Compute Bound): NVIDIA T4, 320 Turing Tensor çekirdeğine sahiptir. Bizim modelimiz (12 milyon parametreli TabNet varyantı), 1 adet isteği işlemek için GPU’yu 1.2ms meşgul ediyordu. Ancak veriyi RAM’den VRAM’e kopyalamak 2.1ms sürüyordu. İstekler tek tek (batch=1) geldiği için GPU sürekli veri bekliyor, çekirdeklerin %82’si atıl kalıyordu.
Temel kural: Eğer GPU tabanlı bir çıkarım senaryosunda GPU Utilization değeriniz %50’nin altındaysa, büyük ihtimalle compute-bound değil, memory bandwidth veya framework I/O darboğazındasınızdır.
Triton Inference Server Mimarisi ve ONNX Dönüşümü
Darboğazı aşmak için PyTorch modelimizi doğrudan Python üzerinden koşturmak yerine, C++ ile yazılmış ve donanım seviyesinde optimizasyon sağlayan NVIDIA Triton’a geçme kararı aldık. PyTorch .pt dosyasını doğrudan kullanmak mümkün olsa da, graph optimizasyonları ve node fusion (katman birleştirme) avantajlarından yararlanmak için modeli ONNX (Open Neural Network Exchange) formatına dönüştürdük.
ONNX Export Süreci
Dönüşüm sırasında dinamik eksenleri (dynamic axes) doğru tanımlamak, Dynamic Batching’in çalışması için kritik bir adımdır. Batch size boyutunu sabit (static) bırakırsanız Triton istekleri gruplayamaz.
import torch
# Modeli memory'e yükle
model = FraudScoringModel()
model.load_state_dict(torch.load("fraud_v4.pt"))
model.eval()
# Örnek dummy input (batch_size=1, features=64)
dummy_input = torch.randn(1, 64, requires_grad=True)
# ONNX export - Dinamik batch size tanımlaması çok önemli
torch.onnx.export(
model,
dummy_input,
"model.onnx",
export_params=True,
opset_version=14,
input_names=['input_features'],
output_names=['fraud_score'],
dynamic_axes={
'input_features': {0: 'batch_size'}, # 0. eksen dinamik
'fraud_score': {0: 'batch_size'}
}
)
Dynamic Batching Konfigürasyonu
Dynamic Batching, Triton’un en hayati özelliğidir. Ağ üzerinden birbirinden bağımsız gelen tekil istekleri (Örn: A kullanıcısının işlemi, B kullanıcısının işlemi), Triton kendi belleğinde milisaniyelik bir pencerede bekletir. Belirlenen limite ulaşıldığında (veya süre dolduğunda) bu istekleri tek bir tensor (örn: [64, 64]) haline getirip GPU’ya tek seferde gönderir. Bu, Host-to-Device kopyalama maliyetini tek bir işleme indirger.
config.pbtxt Yapılandırması
Aşağıda, üretim ortamında kullandığımız konfigürasyon dosyası yer almaktadır. Parametrelerin rastgele seçilmediğine, yük testi sonuçlarına dayandığına dikkat edin.
name: "fraud_scoring_onnx"
platform: "onnxruntime_onnx"
max_batch_size: 128
input [
{
name: "input_features"
data_type: TYPE_FP32
dims: [ 64 ]
}
]
output [
{
name: "fraud_score"
data_type: TYPE_FP32
dims: [ 1 ]
}
]
# Dynamic Batching Kalbi
dynamic_batching {
preferred_batch_size: [ 32, 64, 128 ]
max_queue_delay_microseconds: 5000
}
# Memory pinning ile CPU-GPU kopyalama hızlandırması
optimization {
execution_accelerators {
cpu_execution_accelerator : [ {
name : "openvino"
}]
gpu_execution_accelerator : [ {
name : "tensorrt"
parameters { key: "precision_mode" value: "FP16" }
}]
}
}
instance_group [
{
count: 2
kind: KIND_GPU
gpus: [ 0 ]
}
]
Trade-off Analizi: Hangi Aracı Seçmeli?
Mimari kararı alırken üç ana alternatifi değerlendirdik: FastAPI (Async Batching), Ray Serve ve Triton Inference Server. Hangisini hangi senaryoda kullanmalıyız?
| Kriter | FastAPI + asyncio.gather | Ray Serve | Triton Inference Server |
|---|---|---|---|
| Kurulum & Entegrasyon | Çok kolay (Saf Python) | Orta (Python tabanlı ekosistem) | Zor (Docker, Protobuf, C++ bağımlılıkları) |
| Max Throughput (TPS) | Düşük (~600 TPS/Node) | Orta (~1400 TPS/Node) | Çok Yüksek (~2800 TPS/Node) |
| GPU Kullanım Oranı | %20-30 | %60-70 | %90+ |
| Python Overhead | Yüksek (GIL, JSON Parsing) | Orta (Serileştirme maliyeti) | Yok (Doğrudan C++ / gRPC) |
| İdeal Senaryo | Düşük trafik, basit PoC süreçleri | Çoklu model pipeline’ları, CPU/GPU karma işler | Saf performans, alt-milisaniye SLA’ler |
Eğer modelinizin çıkarım süresi zaten 500ms-1sn sürüyorsa (örn: büyük bir LLM veya resim işleme), Triton’un milisaniyelik kazanımları göz ardı edilebilir. Ancak bizim senaryomuzda bütçe 100ms olduğu için ağ gecikmesi (network latency) ve framework overhead’ini sıfıra indirmek zorunluydu. Bu yüzden öğrenme eğrisi (learning curve) daha dik olan Triton’u seçtik.
Parametre Optimizasyonu ve Performans Testleri
Dynamic Batching parametrelerini ayarlamak bir denge (trade-off) oyunudur. max_queue_delay_microseconds parametresini çok yüksek tutarsanız, boş anlarda gelen istekler batch dolsun diye gereksiz yere bekler. Çok düşük tutarsanız, batch’ler 2’şer 3’er elemanla GPU’ya gider ve throughput düşer.
Kuyruk Gecikmesi (Queue Delay) Test Sonuçları
Aşağıdaki tablo, yük testi ortamında (4500 TPS) farklı max_queue_delay_microseconds değerlerinin p50, p99 ve GPU Utilization üzerindeki etkisini göstermektedir.
- 1000 µs (1ms): p50 18ms, p99 45ms. Ortalama batch size 12’de kaldı. GPU kullanım oranı %65.
- 5000 µs (5ms): p50 12ms, p99 25ms. Ortalama batch size 86. GPU kullanım oranı %88. (Seçilen optimum nokta)
- 15000 µs (15ms): p50 28ms, p99 42ms. Ortalama batch size 128 (max limite ulaştı). GPU kullanım oranı %92.
5ms’lik bekleme süresi, p99 gecikmesini 25ms’ye düşürerek bize SLA limitimiz olan 100ms içinde 75ms’lik bir tampon (buffer) kazandırdı. Bu tamponu, feature engineering aşamasındaki veritabanı (Redis) okumaları için kullandık.
Pratik Öneriler / Production Notları
1. gRPC Kullanımı ve Client Yönetimi
Triton’a HTTP/REST üzerinden istek atmak, JSON serileştirme maliyeti nedeniyle kazanımların bir kısmını çöpe atar. Mutlaka gRPC kullanın ve client bağlantılarını havuzlayın (connection pooling). Her istekte yeni bir gRPC channel açmak, p99’da 30-40ms ekstra gecikme yaratır.
2. Metriklerin İzlenmesi (Prometheus)
Triton, 8002 portu üzerinden native Prometheus metrikleri sunar. Alarm kurmanız gereken en kritik 3 metrik şunlardır:
nv_inference_queue_duration_us: Bu değer sürekli artıyorsa, modelinizin çıkarım hızı gelen trafiğe yetişemiyor demektir.instance_groupcount değerini artırmanız gerekir.nv_inference_compute_input_duration_us: Modelin VRAM’e transfer ve H2D (Host to Device) süresini gösterir. Beklenmedik sıçramalar PCIe darboğazını işaret eder.nv_inference_request_failure: Input shape uyuşmazlıkları veya timeout durumlarını yakalar.
3. Fallback ve Circuit Breaker Stratejisi
Triton servisine ulaşılamadığında veya gecikme 80ms’yi aştığında, işlemi kaybetmemek için bir “Circuit Breaker” tasarladık. Modelden yanıt gelmezse, işlem Redis üzerindeki statik kurallara (rule-engine) düşer (Örn: “Kullanıcının son 24 saatteki 3. işlemi mi?”). Bu sayede model çökse bile sistem degraded-mode (kısıtlı mod) ile %99.99 uptime sağlamaya devam eder.
Sık Sorulan Sorular
Dynamic Batching sırasında gelen ilk istek ile son istek arasındaki bekleme süresi adaletsizliği nasıl çözülür?
Triton’un Default Scheduler’ı FIFO (First-In-First-Out) mantığıyla çalışır. Eğer max_queue_delay_microseconds 5000 (5ms) olarak ayarlanmışsa, kuyruğa ilk giren istek maksimum 5ms bekler. Son giren istek ise belki 0.1ms bekler. Bizim örneğimizde 5ms, sistemin genel bütçesi olan 100ms içinde çok küçük bir dilim olduğu için bu adaletsizlik (jitter) tolere edilebilmektedir.
Model belleğinde sızıntı (Memory Leak) yaşanıyor mu?
FastAPI+PyTorch mimarisinde worker’ların periyodik olarak yeniden başlatılmasını gerektiren (Max requests per child) fragmentation sorunları yaşardık. Triton C++ tabanlıdır ve memory management konusunda oldukça katıdır. Aylarca yeniden başlatılmadan stabil 1.2GB VRAM tüketimiyle çalışan nodelarımız mevcuttur.
CPU üzerinde çalışan modeller için Dynamic Batching işe yarar mı?
Evet, ancak GPU’daki kadar dramatik bir sıçrama beklemeyin. CPU’larda vectorization (AVX-512) sayesinde batch işlemler tekil işlemlere göre daha performanslıdır, ancak CPU’nun memory bandwidth’i GPU (HBM/GDDR) kadar geniş olmadığı için büyük batch size’lar (örn: 128) L3 cache’i taşırarak performansı düşürebilir. CPU için preferred_batch_size değerlerini [4, 8, 16] bandında tutmak daha güvenlidir.
Birden fazla modeli tek Triton instance’ında host edebilir miyim?
Kesinlikle. Triton’un en büyük avantajlarından biri budur. “Model Repository” klasörüne farklı klasörler ekleyerek (örn: Fraud modeli, NLP modeli, Görüntü işleme) hepsini aynı GPU üzerinde, VRAM limitleriniz elverdiği ölçüde koşturabilirsiniz. Triton, modeller arası context-switch maliyetini CUDA stream’ler üzerinden asenkron yöneterek minimize eder.
Sonuç
Machine Learning modellerini production ortamına almak sadece “model.predict()” çağırmaktan ibaret değildir. Yüksek hacimli ve zaman kritik sistemlerde donanımın nasıl yönetildiği, framework sınırları ve I/O darboğazları en az modelin doğruluğu kadar önemlidir. FastAPI’den Triton Inference Server’a geçiş ve Dynamic Batching’in doğru konfigüre edilmesiyle, donanım bütçemizi artırmadan p99 gecikmemizi %91 oranında iyileştirdik.
Uygulamaya hemen geçirmek için şu üç adımı izleyebilirsiniz: Mevcut çıkarım servisinizin metriklerini (Compute vs I/O time) analiz edin, modelinizi dinamik eksenlerle ONNX formatına dönüştürün ve Triton’u load test (örn: k6 veya Locust) ile simüle ederek kendi optimum max_queue_delay değerinizi bulun.
Bunları da beğenebilirsiniz

Edge Cihazlarda YOLOv8 ile Gerçek Zamanlı Nesne Tespiti: Docker ve NVIDIA Jetson Üzerinde Performans Optimizasyonu
Bu kapsamlı rehberde, YOLOv8 modelini kullanarak NVIDIA Jetson edge cihazlarda gerçek zamanlı nesne tespitini nasıl optimize edeceğinizi öğreneceksiniz. Docker ve TensorRT entegrasyonuyla performansı zirveye taşıyın.

Web Uygulamalarında RAG Tabanlı Yapay Zeka Entegrasyonu: Güvenli Dağıtım ve Vektör Veritabanı Optimizasyonları Rehberi
Web uygulamalarınıza RAG (Retrieval Augmented Generation) tabanlı yapay zekayı güvenli bir şekilde entegre etme ve vektör veritabanlarını optimize etme stratejilerini keşfedin. Kullanıcı deneyimini zenginleştirirken güvenlik ve performans sağlamanın yollarını öğrenin.
