Bir LLM'nin uzun bir yanıt oluşturmasını izleyip, ilk istem (prompt) parlamasından sonra neden kaplumbağa hızıyla ilerlediğini merak ettiyseniz, gerçek zamanlı bir donanım darboğazına tanıklık ediyorsunuz demektir. Çoğu geliştirici Python kodunu, framework'ü veya modelin devasa boyutunu suçlar. Fonksiyonları analiz ederler, optimize edicileri değiştirirler ve ön işleme aşamasından milisaniyeler çalarlar. Bunların hiçbiri asıl sorunu çözmez. Hız sınırı yazılımınızda değil, silikonun içindedir.

Her büyük dil modeli çıkarım (inference) işi, sunucunuzda bulunan GPU'nun iki fiziksel özelliğine bağlıdır: sayıları ne kadar hızlı işleyebildiği ve bu sayıları işlenecek konuma ne kadar hızlı getirebildiği.

Matematik Ucuzdur. Veri Taşımak Değil

GPU pazarlaması "hesaplama" (compute) hakkında konuşmaya bayılır. Saniyede trilyonlarca kayan nokta işlemi. Rakamlar dudak uçuklatıcıdır. Ancak hesaplama hikayenin sadece yarısıdır. Diğer yarısı ise bellek bant genişliğidir (memory bandwidth); yani verilerin yüksek bant genişlikli bellekten, asıl aritmetik işlemlerin gerçekleştiği hesaplama çekirdeklerine aktarılma hızıdır.

Bir LLM, bu iki bağlantıdan hangisi daha zayıfsa ondan daha hızlı çalışamaz. Yirmi usta şefin bulunduğu ticari bir mutfak hayal edin. Fırınlar sıcak, bıçaklar keskin ve her aşçı hazır. Ancak sebze teslimatı bisikletle, her seferinde tek bir sepet şeklinde geliyor. Mutfak durma noktasına gelir. Daha fazla şef eklemek bunu çözmez. Daha hızlı fırınlar almak bunu çözmez. Darboğaz yoldur.

Modern veri merkezi GPU'larında aritmetik birimler o kadar güçlüdür ki, genellikle hesaplamalarını bitirirler ve ardından ağırlıkların (weights) ve aktivasyonların bellek üzerinden akmasını beklerken boşta bekleyip döngüleri (cycles) boşa harcarlar. Bu dengesizlik kodunuzdaki bir hata değildir. Bu, çiplerin nasıl inşa edildiğinin fiziksel gerçeğidir. Bellek bant genişliği, ham hesaplama gücüne ayak uyduramadı ve LLM'ler bu dengesizliğe karşı özellikle acımasızdır; çünkü ileri geçişleri (forward passes), her bir çıktı token'ı için her bir parametreye dokunulmasını gerektirir.

İstemler Neden Hızlı, Üretim Neden Yavaş Hissedilir?

LLM çıkarımı iki farklı aşamaya ayrılır ve bunlar donanımı tamamen farklı şekillerde zorlar.

Prefill, isteminiz modele ilk ulaştığında gerçekleşir. Tüm token'lar birlikte gelir. GPU, bunları büyük matris-matris çarpımları kullanarak paralel olarak işleyebilir. Binlerce aritmetik birim aynı anda çalışır ve iş yükü yoğun kalır. Bu aşama hesaplama sınırlıdır (compute-bound). Başlangıçta gördüğünüz o ani hız patlaması mı? İşte o, GPU'nun tam olarak yapması için tasarlandığı şeyi yapmasıdır.

Decode, işlerin sancılı hale geldiği yerdir. Model bir sonraki token'ı oluştururken bunu her seferinde tek bir token olarak yapar. Bu aşama, GPU'nun paralel kapasitesinin yalnızca çok küçük bir kısmını kullanan matris-vektör işlemlerine dayanır. Daha da kötüsü, her yeni token GPU'yu tüm model ağırlıklarını bellekten yeniden yüklemeye zorlar. Aritmetik birimler çalışmak ister; ancak bunun yerine beklerler. Decode, bellek sınırlıdır (memory-bound). GPU, matematik motorları soğurken parametreleri bellek veri yolu (memory bus) üzerinden bir ileri bir geri taşıyan pahalı bir trafik kontrolörü gibi hareket eder. Başlangıçtaki istem analizi anlık hissettirse bile, yüz kelimelik bir yanıtın neden on saniye sürebileceğinin sebebi budur.

KV cache bunu daha da ilginç hale getirir. Decode sırasında model, dikkati (attention) sıfırdan tekrar hesaplamamak için her önceki token için anahtar (key) ve değer (value) tensörlerini saklar. Bu önbellek, dizi uzunluğuyla birlikte büyür. Ayrıca bellekte yaşar. Yani artık GPU sadece ağırlıkları yeniden yüklemekle kalmaz; her bir ileri geçişte sürekli genişleyen bir önbelleği okur ve yazar. Hesaplama çekirdekleri neredeyse hiç terlemezken, bellek veri yolu her ikisi için de ter döker.

Bellek Duvarıyla Savaşmak

Mühendisler, taşınması gereken veri miktarını azaltmak veya en azından taşıma maliyetini paylaşmak için küçük bir teknik cephaneliği geliştirdiler.

Batching (Gruplama) en basit olanıdır. Eğer bir kullanıcının isteği bellekten tam bir ağırlık yüklemesini gerektiriyorsa, sekiz veya on altı isteği aynı anda işlemek, GPU'nun bu yükü hepsine yaymasını sağlar. Ağırlıklar bir kez okunur ve gruptaki her dizi için yeniden kullanılır. Üretim ortamında, bazen "continuous" veya "in-flight batching" olarak adlandırılan gelişmiş zamanlama sistemleri, istekleri dinamik olarak gruplandırarak GPU'nun nadiren duraklamasını sağlar. Bu, aynı rotadaki bir otobüs ile on altı ayrı araç arasındaki fark gibidir.

Quantization attacks the bandwidth problem directly. Model weights are usually stored in sixteen-bit floating-point formats. By compressing them down to eight-bit or even four-bit integers, you literally cut the amount of data traveling across the bus by half or more. The model still needs enough precision to produce coherent output, but modern post-training quantization methods can shrink a model’s memory footprint dramatically without destroying quality. Less data in flight means less time spent waiting at the memory controller.

FlashAttention restructures the attention mechanism to keep intermediate results inside the GPU's fast on-chip memory. Standard attention had to write large attention matrices out to slow external memory and then read them back. FlashAttention breaks the calculation into smaller tiles that fit in SRAM, performs the softmax and scaling steps on-chip, and only writes the final outputs back to high-bandwidth memory. It trades a bit of extra compute for far fewer round trips to main memory, which is almost always a winning bet.

PagedAttention solves a different kind of memory waste. During decode, the KV cache grows unpredictably. Traditional systems allocate fixed, contiguous chunks of memory for each sequence, leaving large holes as some sequences end early and others expand. PagedAttention borrows the concept of virtual memory from operating systems. It stores KV cache entries in fixed-size blocks that can be allocated non-contiguously and mapped through an indirection table. This stops memory from sitting idle inside reserved but half-empty buffers and allows larger batch sizes, which in turn improves overall throughput by keeping the memory bus busy with useful work instead of fragmentation overhead.

Shift the Question

When latency spikes, too many teams ask whether they should switch to a smaller model or rewrite their inference server. Those questions matter, but they are secondary. The first question should be about the hardware itself. Is your GPU actually busy computing, or is it starved for data?

Look at your utilization metrics. Profile memory bandwidth saturation alongside GPU compute occupancy. If you see high memory contention and low arithmetic intensity during decode, you do not have a model architecture problem. You have a physics problem. The solution will not come from cleaner Python. It will come from batching more aggressively, quantizing your weights to squeeze through the pipe faster, restructuring attention to stay on-chip, and managing the KV cache so you can fit larger batches without running out of room.

Once you see inference through this lens, optimization becomes mechanical. You stop chasing myths about model intelligence slowing things down and start making engineering decisions grounded in what the hardware can actually deliver. That is the shift that separates production systems that scale from those that merely function.