Prefill ve Decode Ayrıştırması: LLM Çıkarımında Yeni Dönem
Büyük dil modellerinde (LLM) prefill ve decode aşamalarını farklı donanımlara ayırarak çıkarım performansını ve maliyet verimliliğini nasıl artırabileceğinizi keşfedin.
Yapay zeka dünyasında, büyük dil modellerinin (LLM) üretken yetenekleri her geçen gün daha fazla işletme tarafından kritik iş süreçlerine entegre edilmektedir. Ancak, bu modelleri üretim ortamlarında (production) ölçeklemek, sistem mimarları için ciddi donanım ve maliyet zorluklarını beraberinde getirir. Özellikle, kullanıcıların sistemden beklediği düşük gecikme süresi (latency) ve yüksek işlem hacmi (throughput) taleplerini aynı anda karşılamak geleneksel mimarilerde oldukça güçtür. Bu zorluğun temel sebebi, bir LLM çıkarım (inference) işleminin doğası gereği iki tamamen farklı karakteristiğe sahip aşamadan oluşmasıdır: Prefill (Ön Yükleme) ve Decode (Anlamlandırma).
Geleneksel olarak, bu iki aşama aynı GPU veya donanım kümesi üzerinde sırayla çalıştırılır. Bununla birlikte, bu birleşik yaklaşım donanım kaynaklarının son derece verimsiz kullanılmasına yol açar. Son dönemde geliştirilen “Prefill-Decode Disaggregation” (Ayrıştırılmış Çıkarım Mimarisi) yaklaşımı, bu iki süreci fiziksel olarak farklı donanım kaynaklarına bölerek bu verimsizliği ortadan kaldırmayı hedeflemektedir. Bu makalede, bu yeni mimari yaklaşımın teknik detaylarını, neden bir zorunluluk haline geldiğini ve üretim ortamlarında nasıl uygulanabileceğini derinlemesine inceleyeceğiz.
Büyük Dil Modellerinde Çıkarım Aşamaları Nelerdir?
Büyük dil modelleriyle çalışırken çıkarım süreci dışarıdan tek bir homojen işlem gibi görünse de, arka planda matematiksel olarak çok farklı iki faz gerçekleşir. Bu fazları anlamak, donanım düzeyindeki darboğazları çözmenin ilk adımıdır. Çıkarım süreci, kullanıcının sisteme bir istem (prompt) göndermesiyle başlar ve modelin bu isteme karşılık gelen yanıtı kelime kelime (token bazlı) üretmesiyle devam eder.
Özellikle, bu iki aşamanın donanım üzerindeki kaynak tüketim alışkanlıkları taban tabana zıttır. Birinci aşama işlemci gücünü sonuna kadar zorlarken, ikinci aşama tamamen bellek bant genişliğinin sınırlarına takılır. Bu durum, her iki aşama için optimize edilmiş özel donanım yapılandırmalarının kullanılmasını mantıklı hale getirmektedir. Şimdi bu iki aşamayı daha detaylı ele alalım.
Prefill (Ön Yükleme) Aşaması Nedir ve Nasıl Çalışır?
Prefill aşaması, modelin kullanıcının gönderdiği tüm girdiyi (prompt) tek bir seferde okuyup işlediği ilk adımdır. Örneğin, sisteme 2000 kelimelik bir makale verip özetlemesini istediğinizde, bu 2000 kelimenin tamamı prefill aşamasında işlenir. Bu süreçte, modeldeki tüm dikkat (attention) mekanizmaları girdi token’ları arasındaki ilişkileri hesaplar.
Matematiksel açıdan bakıldığında, prefill aşaması son derece paralel bir yapıya sahiptir. Girdideki tüm token’lar aynı anda işlenebildiği için, bu aşama “compute-bound” yani hesaplama yoğunlukludur. GPU’ların içinde bulunan Tensor çekirdekleri bu aşamada maksimum verimlilikle çalışır. Prefill aşamasının temel çıktılarından biri de, sonraki adımlarda kullanılacak olan KV Cache (Key-Value Önbelleği) verisidir. Bu aşamanın hızı, kullanıcının ilk yanıtı görme süresini (Time to First Token – TTFT) doğrudan belirler. Dolayısıyla, düşük TTFT değerleri elde etmek için çok güçlü hesaplama kapasitesine sahip GPU’lara ihtiyaç duyulur.
Decode (Anlamlandırma) Aşaması ve Bellek Sınırları
Prefill aşaması tamamlandıktan ve ilk token üretildikten sonra, decode aşaması başlar. Decode, modelin bir sonraki token’ı üretmek için kendi ürettiği önceki token’ları da girdi olarak alarak ardışık (autoregressive) bir şekilde çalıştığı aşamadır. Yani, model her adımda sadece tek bir token üretir. Bu durum, sürecin doğasını tamamen değiştirir.
Bu ardışık yapı nedeniyle, decode aşaması paralel olarak yürütülemez. Her yeni token üretilirken, modelin tüm ağırlıklarının (weights) ve o ana kadar birikmiş olan tüm KV Cache verisinin GPU belleğinden (HBM) çekirdek süzgecine tekrar tekrar yüklenmesi gerekir. Bu durum, decode aşamasını tamamen “memory-bandwidth bound” yani bellek bant genişliği sınırlarına bağımlı hale getirir. Sonuç olarak, GPU’nun devasa hesaplama gücü (Tensor çekirdekleri) bu aşamada çoğunlukla boşta kalır ve bellekten veri gelmesini bekler. Decode aşamasının performansı, token’lar arası gecikme süresini (Inter-Token Latency – ITL) belirler ve bu da kullanıcının okuma hızını doğrudan etkiler.
Neden Aynı Donanım Üzerinde Çalıştırmak Verimsizdir?
Geleneksel çıkarım sunucularında, prefill ve decode işlemleri aynı GPU üzerinde dinamik olarak zamanlanır. Ancak, bu durum ciddi bir kaynak çatışmasına (resource contention) yol açar. Örneğin, sistem aktif olarak 50 kullanıcının decode işlemini gerçekleştirirken (yani yavaş yavaş kelime üretirken), aniden sisteme 4000 token’lık yeni ve büyük bir prefill isteği gelebilir. Bu durum, sistem performansında ciddi dalgalanmalara yol açar.
Bu yeni gelen prefill isteği, GPU’nun hesaplama kapasitesini tamamen domine eder. Bu nedenle, devam etmekte olan decode işlemleri duraklamak zorunda kalır. Literatürde buna “Head-of-Line Blocking” (Hat Başı Tıkanıklığı) denir. Kullanıcı açısından bu durum, metin akışında ani takılmalar ve yüksek gecikme dalgalanmaları (jitter) olarak hissedilir. Bununla birlikte, bellek yönetimi de büyük bir sorun haline gelir. Prefill işlemi devasa miktarda KV Cache oluştururken, decode işlemi bu cache’i uzun süre bellekte tutmak ister.
İki farklı bellek kullanım profilinin aynı fiziksel sınırları paylaşması, bellek parçalanmasına (fragmentation) ve erken Out-Of-Memory (OOM) hatalarına yol açar. vLLM gibi modern kütüphaneler PagedAttention ile bellek parçalanmasını çözse de, aynı donanım üzerindeki zamanlama çatışmasını tamamen çözemezler. Bu nedenle, iki aşamayı tek bir donanımda birleştirmek, her iki aşamanın da kendi potansiyelinin altında performans göstermesine neden olur. Sonuç olarak, donanım kaynakları israf edilirken kullanıcı deneyimi de olumsuz etkilenir.
Prefill ve Decode Ayrıştırması (Disaggregation) Nasıl Yapılır?
Ayrıştırılmış çıkarım mimarisi (Disaggregated Inference), çıkarım kümesini (cluster) iki özel gruba ayırarak bu sorunu kökten çözer: Prefill Düğümleri (Nodes) ve Decode Düğümleri. Bu mimaride, bir istek sisteme ulaştığında süreç akıllı bir yük dengeleyici ve yönlendirici aracılığıyla yönetilir. Sürecin işleyiş adımları şu şekildedir:
- İstek Kabulü ve Yönlendirme: Kullanıcının gönderdiği istem (prompt) ilk olarak sadece prefill için optimize edilmiş güçlü düğümlere yönlendirilir.
- Hızlı Hesaplama ve KV Cache Oluşturma: Prefill düğümü, yüksek hesaplama gücünü kullanarak istemi hızla işler, ilk token’ı üretir ve ilişkili KV Cache verisini oluşturur.
- KV Cache Transferi: Oluşturulan KV Cache verisi, yüksek hızlı ağ altyapısı üzerinden uygun bir decode düğümüne aktarılır.
- Ardışık Üretim: Decode düğümü, aldığı KV Cache verisini kullanarak sonraki token’ları üretmeye devam eder ve prefill düğümünü yeni istekler için tamamen serbest bırakır.
Bu sayede, prefill düğümleri sadece yüksek işlem gücü gerektiren işlere odaklanırken, decode düğümleri de bellek bant genişliği optimize edilmiş donanımlarla kesintisiz bir şekilde kelime üretimi gerçekleştirir. Böylece, hem TTFT hem de ITL değerlerinde dramatik iyileşmeler elde edilir. Ayrıca, her iki düğüm grubu da kendi iş yüklerine göre bağımsız olarak ölçeklendirilebilir.
KV Cache Transferi ve Ağ Altyapısının Önemi
Ayrıştırılmış çıkarım mimarisinin en kritik teknik meydan okuması, KV Cache verisinin düğümler arasında taşınmasıdır. Çünkü büyük modellerde ve uzun bağlam pencerelerinde (context window) KV Cache boyutu gigabaytlar seviyesine ulaşabilir. Eğer bu veri transferi yavaş gerçekleşirse, ayrıştırmadan elde edilecek performans kazancı ağ gecikmesi nedeniyle tamamen kaybolabilir.
Bu engeli aşmak için modern veri merkezlerinde yüksek hızlı ağ teknolojileri kullanılır. Özellikle, InfiniBand veya RoCE v2 (RDMA over Converged Ethernet) gibi teknolojiler, CPU’yu tamamen devre dışı bırakarak doğrudan GPU bellekleri arasında (GPUDirect RDMA) veri transferi sağlar. Bu sayede, milisaniyeler mertebesinde gigabaytlarca veri aktarılabilir. Dolayısıyla, başarılı bir ayrıştırılmış mimari kurulumu sadece doğru GPU seçimine değil, aynı zamanda ultra düşük gecikmeli ağ altyapısının tasarlanmasına da doğrudan bağlıdır.
Gerçek Dünya Senaryosu: Ölçeklenebilir Bir Altyapı Analizi
Gelin, gerçek bir üretim ortamı senaryosunu inceleyelim. Büyük bir e-ticaret platformunun, müşterilerine anlık destek veren ve saniyede yüzlerce istek alan bir LLM altyapısına sahip olduğunu varsayalım. Geleneksel birleşik mimari ile ayrıştırılmış mimari arasındaki farkları karşılaştırmak, bu değişimin iş sonuçlarına etkisini net bir şekilde gösterecektir.
Bu senaryoda, sistemin ortalama girdi boyutu 3000 token, çıktı boyutu ise 500 token’dır. Aşağıdaki tabloda, her iki mimarinin performans ve maliyet metrikleri karşılaştırılmıştır:
| Metrik | Birleşik Mimari (H100 Kümesi) | Ayrıştırılmış Mimari (H100 Prefill + L40S Decode) |
|---|---|---|
| Time to First Token (TTFT) | ~850 ms | ~150 ms |
| Inter-Token Latency (ITL) | ~45 ms (Yüksek Dalgalanma) | ~12 ms (Kararlı ve Akıcı) |
| Donanım Maliyeti (Aylık) | Yüksek (Sadece H100 kullanımı) | %30 ila %40 Daha Düşük |
| Sistem Verimliliği (Throughput) | Düşük (GPU’lar sık sık bloke olur) | %45 Daha Yüksek |
Tablodan da anlaşılacağı üzere, ayrıştırılmış mimari sadece performansı artırmakla kalmaz, aynı zamanda decode aşaması için daha ucuz ama bellek bant genişliği yüksek olan donanımların (örneğin L40S veya L4) kullanılabilmesine olanak tanıyarak toplam sahip olma maliyetini (TCO) ciddi oranda düşürür. Bu durum, özellikle milyonlarca kullanıcıya hizmet veren büyük ölçekli sistemlerde devasa bütçe tasarrufları anlamına gelir.
Python ile Basit Bir Ayrıştırılmış Çıkarım Simülasyonu
Ayrıştırılmış çıkarım sistemlerinin arkasındaki mantığı daha iyi anlamak için, bir yönlendirici (scheduler) ve iki farklı işçi düğümün (worker) nasıl çalıştığını simüle eden basit bir Python kodu inceleyelim. Bu örnek, ağ üzerinden KV Cache aktarım mantığını soyut bir şekilde göstermektedir.
import time
class PrefillNode:
def __init__(self, node_id):
self.node_id = node_id
def process_prompt(self, prompt):
print(f"[Prefill-{self.node_id}] '{prompt}' işleniyor (Compute-bound)...")
time.sleep(0.5) # Ağır hesaplama simülasyonu
kv_cache = f"kv_cache_data_for_{hash(prompt)}"
first_token = "Merhaba"
return kv_cache, first_token
class DecodeNode:
def __init__(self, node_id):
self.node_id = node_id
def generate_tokens(self, kv_cache, first_token, max_tokens=5):
print(f"[Decode-{self.node_id}] KV Cache alındı. Kelime üretimi başlıyor (Memory-bound)...")
generated_text = [first_token]
for i in range(max_tokens):
time.sleep(0.1) # Bellek okuma simülasyonu (yavaş ama ardışık)
next_token = f"token_{i}"
generated_text.append(next_token)
print(f"[Decode-{self.node_id}] Üretilen: {next_token}")
return " ".join(generated_text)
class InferenceScheduler:
def __init__(self, prefill_node, decode_node):
self.prefill_node = prefill_node
self.decode_node = decode_node
def run_inference(self, prompt):
# 1. Aşama: Prefill
kv_cache, first_token = self.prefill_node.process_prompt(prompt)
# 2. Aşama: KV Cache Transferi (Ağ Gecikmesi)
print("[Scheduler] KV Cache ağı üzerinden Decode düğümüne aktarılıyor...")
time.sleep(0.05) # Ağ transfer simülasyonu
# 3. Aşama: Decode
final_output = self.decode_node.generate_tokens(kv_cache, first_token)
return final_output
# Sistemi simüle edelim
scheduler = InferenceScheduler(PrefillNode(1), DecodeNode(1))
output = scheduler.run_inference("LLM mimarileri nasıl çalışır?")
print(f"Sonuç: {output}")
Yukarıdaki kod simülasyonunda açıkça görüldüğü gibi, prefill düğümü ağır hesaplama yükünü tamamladıktan sonra, elde edilen verileri decode düğümüne devreder. Bu sayede prefill düğümü, yeni bir isteğin ön yükleme aşamasını gerçekleştirmek için anında boşa çıkmış olur. Gerçek dünya uygulamalarında bu süreç, binlerce eşzamanlı istek için dinamik kuyruk yönetimi ve gelişmiş ağ protokolleri ile optimize edilir.
Geleceğin LLM Altyapıları: Bizi Neler Bekliyor?
Yapay zeka modellerinin boyutları ve bağlam pencereleri (context window) büyüdükçe, prefill ve decode ayrıştırması lüks olmaktan çıkıp bir endüstri standardı haline gelecektir. Özellikle 1 milyon token’a ulaşan bağlam pencerelerinde, KV Cache boyutları devasa boyutlara ulaşmaktadır. Bu durum, dağıtık bellek yönetimini ve ayrıştırılmış mimarileri zorunlu kılmaktadır.
Gelecekte, bulut sağlayıcılarının doğrudan “Prefill-as-a-Service” ve “Decode-as-a-Service” gibi ayrıştırılmış API hizmetleri sunduğunu göreceğiz. Bununla birlikte, vLLM, TensorRT-LLM ve Hugging Face TGI gibi popüler çıkarım motorları, bu tür ayrıştırılmış mimarileri kutudan çıktığı haliyle (out-of-the-box) desteklemek için altyapılarını hızla güncellemektedir. Donanım üreticileri de artık sadece ham güç odaklı değil, bu tür ağ bazlı bellek transferlerini hızlandıracak özel çipler ve ara bağlantı teknolojileri üzerinde çalışmaktadır. Sonuç olarak, yapay zeka çıkarım maliyetleri düşecek ve daha erişilebilir hale gelecektir.
Sıkça Sorulan Sorular (SSS)
Soru 1: KV Cache transferi ağ üzerinde çok fazla gecikmeye neden olmaz mı?
Cevap: Evet, eğer standart bir ağ altyapısı kullanılırsa gecikme yüksek olabilir. Ancak, üretim ortamlarında InfiniBand veya RoCE v2 gibi ultra hızlı ağ protokolleri ve GPUDirect RDMA teknolojisi kullanıldığı için bu transfer milisaniyeler içinde tamamlanır ve elde edilen performans kazancı ağ gecikmesinden çok daha büyüktür.
Soru 2: Bu mimari her LLM projesi için gerekli midir?
Cevap: Hayır, gerekli değildir. Eğer düşük trafikli, küçük bağlam pencereli (örneğin sadece kısa mesajlar) veya gerçek zamanlı olmayan toplu (batch) işlemler yapıyorsanız, geleneksel birleşik mimari sizin için yeterli ve daha az karmaşık olacaktır. Bu mimari, yüksek trafikli ve düşük gecikme gerektiren ölçekli projeler için idealdir.
Soru 3: Decode aşaması için neden daha ucuz GPU’lar tercih edilebilir?
Cevap: Çünkü decode aşaması yüksek hesaplama gücü (TFLOPS) gerektirmez; daha ziyade yüksek bellek bant genişliğine ihtiyaç duyar. Dolayısıyla, H100 gibi çok pahalı ve yüksek hesaplama gücüne sahip GPU’lar yerine, bellek kapasitesi yüksek ama işlem gücü daha mütevazı olan L40S gibi maliyet etkin GPU’lar decode aşaması için mükemmel bir fiyat/performans dengesi sunar.