

Giriş
2023’ün 3. çeyreğinde Llama-2-70B modelini 4 adet A100 80GB GPU üzerinde production ortamına ilk aldığımızda, her şey kağıt üzerinde kusursuz görünüyordu. Stres testlerinde saniyede 1200 token üretimi (throughput) görüyorduk. Ancak gerçek kullanıcı trafiği gelmeye başladığında, 4. saatin sonunda sistem ardışık CUDA OOM (Out of Memory) hatalarıyla çöktü. Sorun modelin boyutu değildi; sorun, PagedAttention mimarisinin bellek yönetim mekaniğini tam olarak kavramadan varsayılan (default) vLLM konfigürasyonlarıyla yola çıkmamızdı.
vLLM, LLM sunumu (serving) dünyasında standart bir araç haline geldi. İşletim sistemlerindeki sanal bellek (virtual memory) ve sayfalama (paging) mantığını GPU VRAM’ine taşıyan PagedAttention algoritması, bellek israfını teorik olarak sıfıra yaklaştırıyor. Ancak production ortamı, teorinin sınırlarının zorlandığı yerdir. Trafik asimetrikleştiğinde, prompt uzunlukları dalgalandığında ve eşzamanlı istek (concurrency) sayısı fırladığında, yanlış yapılandırılmış bir vLLM instance’ı VRAM darboğazına girerek tüm pipeline’ı felç eder.
Aşağıda, yüzlerce saati bulan debug seanslarından, çöken pod’lardan ve 03:00’te çalan PagerDuty alarmlarından süzülmüş, vLLM ile çalışırken mühendislerin düştüğü 5 spesifik PagedAttention ve VRAM tuzağını inceleyeceğiz.
İçindekiler
- Tuzağı 1: %90 gpu_memory_utilization Ölüm Tuzağı
- Tuzağı 2: block_size ve İç Parçalanma (Internal Fragmentation) Körlüğü
- Tuzağı 3: Sistem Prompt’u Amnezisi (Eksik Prefix Caching)
- Tuzağı 4: PCIe Üzerinden CPU RAM Swapping Bataklığı
- Tuzağı 5: NVLink Olmadan Tensor Parallelism (TP) Kullanımı
- Production Notları ve Metrik Stratejisi
- Sık Sorulan Sorular
- Sonuç
Tuzağı 1: %90 gpu_memory_utilization Ölüm Tuzağı
Neden Olur?
vLLM başlatıldığında, VRAM’i dinamik olarak bölmez. Önceden tahsis (pre-allocation) yapar. Model ağırlıklarını yükler, küçük bir forward-pass profillemesi çalıştırır ve geriye kalan VRAM’i KV Cache blokları için rezerve eder. Varsayılan gpu_memory_utilization=0.90 (v0.4.0 öncesi 0.95 idi) ayarı, GPU belleğinin %90’ının vLLM tarafından kilitlenmesi demektir. Geriye kalan %10’luk kısım (80GB bir kartta 8GB), PyTorch’un forward pass sırasındaki ara aktivasyonları (activations) ve NCCL iletişim buffer’ları için bırakılır.
Eğer API’nize aniden 32,000 tokenlik bir bağlam (context) gelirse, attention matrisinin hesaplanması sırasında gereken anlık bellek fırlar. PyTorch, vLLM’in bıraktığı 8GB’tan fazlasına ihtiyaç duyduğunda, KV Cache zaten VRAM’i işgal ettiği için OOM hatası fırlatır.
Nasıl Tespit Edilir?
Loglarınızda tam olarak şu pattern’i görürsünüz:
RuntimeError: CUDA out of memory. Tried to allocate 4.00 GiB. GPU 0 has a total capacity of 79.35 GiB of which 1.12 GiB is free. Process 19323 has 78.23 GiB memory in use.
Prometheus metriklerinde vllm:num_requests_running aniden 0’a düşer ve pod yeniden başlatılır (CrashLoopBackOff).
Nasıl Çözülür?
Modelinizin desteklediği maksimum sekans uzunluğuna (max model len) ve batch size’a göre VRAM’de boşluk bırakmalısınız.
# Tehlikeli yaklaşım
python -m vllm.entrypoints.openai.api_server --model meta-llama/Meta-Llama-3-8B --gpu-memory-utilization 0.95
# Güvenli ve stabil yaklaşım (Production)
python -m vllm.entrypoints.openai.api_server
--model meta-llama/Meta-Llama-3-8B
--gpu-memory-utilization 0.85
--max-model-len 16384
--max-num-seqs 128
Gerçek Olay Örneği
Hukuk metinleri özetleyen bir servisimizde, 8x A100 node’unda Mistral-7B-Instruct kullanıyorduk. Metinler genellikle 2,000 token civarıydı, ancak bir müşteri 28,000 tokenlik bir PDF yüklediğinde tüm batch OOM yedi. gpu_memory_utilization değerini 0.90’dan 0.80’e çektik ve --max-num-seqs parametresini 256’da limitleyerek PyTorch aktivasyonlarına yeterli nefes alma alanını açtık.
Tuzağı 2: block_size ve İç Parçalanma (Internal Fragmentation) Körlüğü
Neden Olur?
PagedAttention, KV Cache’i sabit boyutlu bloklara böler. vLLM’de varsayılan block_size değeri 16’dır (her blok 16 tokenin Key ve Value tensörlerini tutar). Llama-3-8B (FP16) için tek bir tokenin KV Cache boyutu yaklaşık 128 KB’tır. Bir blok 16 token tuttuğu için 2 MB yer kaplar.
Eğer ürettiğiniz veya işlediğiniz sekans 17 token uzunluğundaysa, PagedAttention iki blok (32 tokenlik yer) tahsis eder. İkinci bloğun 15 tokenlik (yaklaşık 1.9 MB) kısmı boştur. Buna iç parçalanma (internal fragmentation) denir. Eğer RAG tabanlı, kısa prompt/kısa cevap (örneğin 10-20 tokenlik extraction görevleri) yapan bir sisteminiz varsa ve eşzamanlı binlerce request alıyorsanız, VRAM’inizin %30-40’ı sadece boş blok pad’leriyle dolar.
Nasıl Tespit Edilir?
Prometheus metriklerinde vllm:gpu_cache_usage_perc %99’da takılıdır, ancak vllm:num_requests_running (aktif batch) beklenen sayının çok altındadır (örneğin 50-60). Throughput (token/sn) düşük seyreder.
Nasıl Çözülür?
Workload’unuzun karakteristiğine göre Trade-off Analizi yapmalısınız:
- Kısa ve değişken uzunlukta sekanslar (RAG extraction, Chat):
--block-size 8kullanın. İç parçalanmayı azaltır, daha fazla isteği batch’e sokarsınız. - Uzun ve tahmin edilebilir sekanslar (Kod tamamlama, makale yazımı):
--block-size 16veya32kullanın. Blok boyutu küçüldükçe, blok tablosunun (block table) CPU/GPU üzerindeki metadata boyutu ve yönetimi zorlaşır, bu da overhead yaratır.
Gerçek Olay Örneği
Bir e-ticaret botunda, kullanıcıların tek kelimelik (“Evet”, “Kargo nerede”) sorularına yanıt üretiyorduk. Yanıtlar ortalama 12 token sürüyordu. Varsayılan 16 block_size ile her 12 tokenlik cevap 16’lık blok işgal ediyordu. --block-size 8 konfigürasyonuna geçerek, VRAM israfını azalttık ve eşzamanlı işlenen istek sayısını (concurrency) 180’den 245’e çıkararak throughput’ta %36’lık bir artış yakaladık.
Tuzağı 3: Sistem Prompt’u Amnezisi (Eksik Prefix Caching)
Neden Olur?
Kurumsal LLM uygulamalarında (özellikle ajan sistemlerinde), her isteğin başına 2,000 ila 4,000 token uzunluğunda ağır bir sistem prompt’u eklenir. vLLM varsayılan ayarlarında bu özelliği açık getirmez. Eğer --enable-prefix-caching parametresini eklemezseniz, tamamen aynı olan bu 4,000 tokenlik sistem prompt’u saniyede 100 farklı istek için 100 kere baştan hesaplanır (Prefill phase). Bu, GPU’nun stream multiprocessor’larını (SM) gereksiz matris çarpımlarıyla boğar.
Nasıl Tespit Edilir?
TTFT (Time To First Token) metriklerinizi inceleyin. Eğer p99 TTFT değeriniz 1 saniyenin üzerindeyse ve CPU kullanımınız GPU kullanımınıza göre orantısız yüksek değilse, muhtemelen devasa prompt’ları tekrar tekrar işliyorsunuzdur. Loglarda prefill sürelerinin uzunluğu göze çarpar.
Nasıl Çözülür ve Trade-off Nedir?
Açıkça RadixTree tabanlı prefix caching’i aktifleştirin.
# kubernetes deployment.yaml örneği
args:
- "--model"
- "Qwen/Qwen2.5-14B-Instruct"
- "--enable-prefix-caching"
- "--disable-custom-all-reduce" # Genellikle multi-node ortamda stabilite için
Trade-off: Prefix caching aktif edildiğinde, her gelen isteğin prompt’u RadixTree üzerinde CPU tabanlı bir eşleşme arayışına girer. Eğer istekleriniz tamamen benzersizse (hiç ortak prefix yoksa), bu arama işlemi her isteğe fazladan ~2ms gecikme (latency) ekler ve LRU eviction mekanizması VRAM’i thrash edebilir. Ortak sistem prompt’unuz varsa kesinlikle açın; sıfırdan text-generation yapıyorsanız kapalı tutun.
Gerçek Olay Örneği
Bir müşteri destek asistanında, “Şirket iade politikası” ve “Davranış kuralları” içeren 3,200 tokenlik sabit bir sistem prompt’u kullanılıyordu. Prefix caching olmadan p95 TTFT 850ms idi. Parametreyi aktifleştirdiğimizde ilk istek 850ms sürdü, ancak sonraki tüm istekler için prefill atlanarak TTFT 120ms’ye düştü.
Tuzağı 4: PCIe Üzerinden CPU RAM Swapping Bataklığı
Neden Olur?
PagedAttention’ın en güçlü özelliklerinden biri, GPU VRAM’i dolduğunda (preemption) aktif sekansları iptal etmek yerine CPU RAM’ine (swap space) taşımasıdır. Varsayılan olarak vLLM 4GB swap alanı ayırır. Ancak bir yük sıçraması (traffic spike) yaşandığında, VRAM dolarsa vLLM blokları PCIe üzerinden CPU’ya yazar ve okur.
PCIe Gen4 x16’nın teorik bant genişliği saniyede 32 GB’tır. Bir A100’ün kendi iç HBM bant genişliği ise saniyede 2,000 GB’tır (2TB/s). KV Cache sürekli CPU’ya gidip gelmeye başladığında, GPU’nun hesaplama üniteleri açlıktan (starvation) ölür. Throughput çakılır, latency saniyelere, hatta dakikalara çıkar.
Nasıl Tespit Edilir?
Prometheus metriklerinizdeki en kritik uyarıcı: vllm:num_requests_swapped. Bu metrik 0’ın üzerine çıktığı an, performansınız uçurumdan aşağı düşüyor demektir. Aynı anda GPU utilization %20’lere geriler.
Nasıl Çözülür?
Swapping, LLM sunumunda bir özellik değil, bir son çare kurtarma operasyonudur. Sürekli gerçekleşmesine izin verilmemelidir.
- API Gateway (örn. HAProxy, Nginx) veya load balancer seviyesinde sert bir Rate Limiting uygulayın.
- vLLM’de
--max-num-seqsdeğerini düşürün. Kuyruğa alma işleminin (queuing) vLLM’in içinde swap ile yapılmasındansa, API Gateway’de beklemesi daha sağlıklıdır. - Swap’ı kapatmak için
--swap-space 0yapabilirsiniz, böylece sistem swap yerine direktPreemptedstatüsüne geçirip reject eder (Fail-fast mantığı).
Gerçek Olay Örneği
Büyük bir haber sitesinin seçim gecesi yayınında, anlık kullanıcı sayısı 10 katına çıktı. vLLM VRAM’i tüketti ve swap yapmaya başladı. Normalde 60 token/sn olan üretim hızı, PCIe darboğazı yüzünden 4 token/sn’ye düştü. API timeout’lar havada uçuştu. Acil durum müdahalesi ile max-num-seqs 256’dan 128’e çekildi ve load balancer seviyesinde istekler sıraya alındı (shedding). Swapping durunca GPU tekrar 60 token/sn hızına ulaştı.
Tuzağı 5: NVLink Olmadan Tensor Parallelism (TP) Kullanımı
Neden Olur?
Büyük modeller (örn. Llama-3-70B) tek bir GPU’ya sığmadığında modelleri böleriz. En yaygın bölme yöntemi Tensor Parallelism’dir (TP). TP, Megatron-LM mimarisini temel alır ve matris çarpımlarını GPU’lara dağıtır. Ancak kritik detay şudur: TP, her transformer katmanında (layer) bir All-Reduce operasyonu gerektirir. 80 katmanlı bir modelde, sadece tek bir forward pass için 80 kez GPU’ların kendi aralarında senkronize olması gerekir.
Eğer TP’yi, aralarında NVLink (saniyede 600 GB çift yönlü bağlantı) bulunmayan, sadece PCIe veya ağ (Ethernet) üzerinden bağlı GPU’lar arasında kurarsanız, All-Reduce iletişimi saniyeler sürer. PagedAttention ne kadar hızlı KV cache getirse de, hesaplama ağ darboğazına takılır.
Nasıl Tespit Edilir?
Çoklu GPU başlatıldığında NCCL (NVIDIA Collective Communications Library) timeout hataları görülür veya model başlar ama Time Per Output Token (TPOT) metriği 500ms – 1000ms gibi absurd seviyelere çıkar. nvidia-smi topo -m komutu çalıştırıldığında GPU’lar arasında NV4 veya NV12 yerine PHB, PIX veya SYS bağlantısı görünür.
Nasıl Çözülür?
Topology-aware (topoloji farkındalıklı) paralelizm stratejisi kullanılmalıdır:
- Aynı Node, NVLink Var:
--tensor-parallel-size Xkullanın. (Örn: 4x A100 NVLink node’u için-tp 4) - Farklı Node’lar veya PCIe Sadece: Pipeline Parallelism (PP) kullanın. PP, modeli katman bazında böler. Sadece aktivasyon sınırlarında ağ üzerinden veri geçer, katman içindeki ağır iletişim önlenir. vLLM v0.4.1 sonrasında PP desteği olgunlaşmıştır.
--pipeline-parallel-size Xkullanın.
Gerçek Olay Örneği
Bir on-premise veri merkezinde, iki ayrı sunucuda yer alan (her birinde 2x RTX 4090) toplam 4 adet GPU üzerine Mixtral 8x7B kurmaya çalıştık. Ray cluster kurup -tp 4 verdik. Node’lar arası 10Gbit ağ vardı. Üretim hızı 1.2 token/sn oldu (neredeyse insan okuma hızından yavaş). Stratejiyi -tp 2 (her node kendi içinde TP yapsın) ve -pp 2 (node’lar arası PP yapılsın) olarak değiştirdiğimizde hız 38 token/sn seviyesine fırladı.
Production Notları ve Metrik Stratejisi
Yukarıdaki tuzaklardan kaçınmak için observability (gözlemlenebilirlik) kritik öneme sahiptir. vLLM, /metrics endpoint’i üzerinden Prometheus formatında mükemmel veriler sunar. Production ortamında şu 3 kuralı işletmelisiniz:
Kritik Prometheus Alarmları (Alerts)
| Metrik / PromQL Sorgusu | Kritik Eşik (Threshold) | Aksiyon / Anlamı |
|---|---|---|
vllm:gpu_cache_usage_perc |
> %95 | KV Cache dolmak üzere. Olası swap veya OOM tehlikesi. Trafiği yönlendir. |
rate(vllm:num_requests_swapped[1m]) |
> 0 | Derhal Rate Limit devreye sokulmalı. GPU thrashing yaşıyor. |
histogram_quantile(0.99, rate(vllm:time_to_first_token_seconds_bucket[5m])) |
> 1.5s | Prefill darboğazı. Prefix caching kapalı olabilir veya batch size çok yüksek. |
Fallback Stratejisi
vLLM pod’ları OOM yediğinde (CrashLoopBackOff durumuna düştüğünde) tekrar ayağa kalkıp modelleri yüklemesi (ağırlıkların safedensors’dan RAM’e, oradan VRAM’e kopyalanması) ağ hızına bağlı olarak 2 ila 5 dakika sürer. Bu süreçte zero-downtime sağlamak için load balancer arkasında en az N+1 pod bulundurun ve Kubernetes health check (/health endpoint’i) kullanın.
Sık Sorulan Sorular
VRAM tasarrufu için AWQ, GPTQ veya FP8 kuantizasyonu (quantization) PagedAttention’ı bozar mı?
Hayır, bozmaz. Hatta vLLM, kuantize edilmiş ağırlıklarla harika çalışır (Özellikle Marlin ve FP8). Model ağırlıkları daha az VRAM kapladığı için (örneğin FP16’da 15GB kaplayan model 4-bit AWQ ile 4.5GB kaplar), gpu_memory_utilization sabit tutulduğunda KV Cache için devasa bir VRAM alanı boşa çıkar. Bu da batch size’ı dramatik ölçüde artırmanızı sağlar.
Continuous Batching (Sürekli Gruplama) TPOT’u (Time per Output Token) nasıl etkiler?
Continuous batching, biten bir isteğin yerine hemen yenisini (prefill aşamasını) sokar. Bu, sistemin throughput’unu (saniye başına toplam token) maksimize eder ancak yeni gelen isteğin prefill (attention) hesaplaması çok ağırsa, halihazırda üretim (decode) aşamasında olan diğer isteklerin o anlık token üretimini geciktirebilir (TPOT artar). vLLM’de --chunked-prefill özelliğini aktif ederek prefill işlemini parçalara bölebilir ve decode aşamasındaki gecikmeleri yumuşatabilirsiniz.
PagedAttention her senaryoda standart HuggingFace (HF) pipeline’larından daha mı iyidir?
Eğer batch_size=1 ise (örneğin sadece sizin kullandığınız lokal bir makine), standart HF generate() fonksiyonu ile vLLM arasında belirgin bir fark yoktur. PagedAttention’ın gerçek gücü eşzamanlı istek (concurrency) sayısı 10’un, 50’nin, 100’ün üzerine çıktığında ve bellek yönetimi imkansızlaştığında ortaya çıkar.
vLLM’i nasıl yatayda (horizontally) ölçekleyebilirim?
vLLM’in kendisi tek bir inference engine’dir. Yatay ölçekleme (birden fazla instance) için önüne FastAPI tabanlı bir router veya özel LLM load balancer’lar (örneğin LiteLLM veya Ray Serve) koymalısınız. İstekleri stateless (durumsuz) olarak dağıtabilirsiniz.
Sonuç
vLLM ve PagedAttention, LLM deployment standartlarını kökünden değiştirdi. Ancak arka plandaki C++ ve CUDA C memory allocator mantığını anlamadan sadece python -m vllm komutunu çalıştırmak, production ortamında patlamaya hazır bir saatli bomba yaratmaktır. %90 bellek kullanımından kaynaklı OOM hataları, yanlış block_size yüzünden %30’a varan VRAM israfı, eksik prefix caching kaynaklı 800ms’lik gecikmeler, swap bataklığı ve yanlış paralelizasyon topolojileri mühendislerin en sık düştüğü çukurlardır.
Aksiyon Planı: Yarın sabah ilk iş, production environment’ınızdaki vLLM konfigürasyonunuzu açın. gpu_memory_utilization değerinizin model max_len’i için yeterli boşluk bırakıp bırakmadığını kontrol edin, --enable-prefix-caching parametresini değerlendirin ve Prometheus üzerinden vllm:num_requests_swapped metrik alarmının açık olduğundan emin olun. Donanımınız ne kadar güçlü olursa olsun, onu yöneten yapılandırma kadardır.
Bunları da beğenebilirsiniz

PHP ile TC Kimlik Numarası Doğrulama Fonksiyonu
Türkiye Cumhuriyeti Kimlik Numarası (TC Kimlik No), ülkemizde bireylerin kimliğini doğrulamak için kullanılan 11 haneli benzersiz bir sayısal koddur. Web uygulamaları geliştirenler için, bu numaranın…

PHP ile Brute Force (Kaba Kuvvet) Saldırısına Karşı Önlem Alma
PHP ile kullanıcılarımızın oturum açmalarını içeren sistemler kuruyoruz. Bu sistemlerde alabileceğimiz saldırılardan en yaygın olanlardan birisi Brute Force yani kaba kuvvet saldırısıdır. Brute Force saldırısı,…

Kurumsal Refactoring İçin Depo Seviyesinde AI Ajanları: İnsan-Döngüde (Human-in-the-Loop) Geri Bildirim Mekanizmaları Tasarlamak
Kurumsal yazılım projelerinde teknik borcu azaltmak için depo seviyesinde otonom ajanların nasıl tasarlanacağını ve insan denetimiyle güvenli refactoring süreçlerinin nasıl işletileceğini inceleyin.