Ayrıştırılmış ön doldurma ve kod çözme
Ön doldurma ve kod çözme işlemlerini cihazlar arasında ayırmak LLM çıkarımını hızlandırır ve performansı artırır
Girdi isteminden çıkış belirteçleri üretmek için LLM çıkarımı iki aşamaya ayrılır: ön doldurma (prefill) ve kod çözme (decode). Ön doldurma, KV önbelleklerini doldurarak girdi belirteçleri üzerinde çalışır ve ardından belirteçleri teker teker üreten kod çözme aşamasına geçilir. Tek bir kod çözme adımı genellikle onlarca milisaniye sürerken, ön doldurma önemli ölçüde daha uzun sürer. Aynı cihazlarda çalıştırıldığında, ön doldurmanın kod çözme ile karıştırılması kod çözme performansını düşürür. Bu makalede, ön doldurma verimliliğini ve kod çözme gecikmelerini en üst düzeye çıkarmak için bunları ayrı cihazlarda çalıştıran, ayrıştırılmış ön doldurma ve kod çözme biçimindeki kökleşmiş bir çözüm incelenmektedir.
Ön Doldurma ve Kod Çözme Performansı
Tipik bir LLM sunum motorunda, toplu iş zamanlayıcısı bir modelin her yürütme adımında işlenecek istekleri seçer. Tek bir cihaz veya düğüm üzerinde çalıştırıldığında, hem ön doldurma hem de kod çözme istekleri birlikte gruplandırılır. Dizi uzunluğu boyunca birleştirme yapan dikkat (attention) maliyeti, KV önbelleğindeki girdi uzunluklarına (kv_len) orantılı olarak hem ön doldurma hem de kod çözme için artar. Kod çözme istekleri genellikle tek bir belirteci (qo_len=1) iletir ve bir dizinin belirteçleri üzerinde bağımsız olarak çalışan diğer katmanlar aracılığıyla minimum maliyet oluşturur. Ön doldurma istekleri ise yoğun katmanlar aracılığıyla önemli bir maliyetle binlerce veya on binlerce belirteci iletir (büyük qo_len).
İleri geçişin (forward pass) gecikmesi, dikkat sırasında KV önbelleğinden alınan belirteç sayısından (kv_len) ziyade, yoğun katmanlardan geçen bağımsız belirteç sayısından (qo_len) daha güçlü bir şekilde etkilenir. Dikkat mekanizması hem istek sayısı hem de dizi uzunluklarına orantılı kv_len boyunca paralelleşebilir ve iyi bir kullanım oranı elde eder. Ön doldurma işlem ağırlıklıdır (compute bound): qo_len yüksek olduğundan, GEMM çekirdekleri modern GPU\'ların işlem yeteneklerini tam olarak kullanmak için M boyutu boyunca yeterli bloklar ayırabilir. Kod çözme ise bellek ağırlıklıdır (memory bound): tipik olarak düşük toplu iş boyutları nedeniyle, M üzerindeki girdi sayısı genellikle küçüktür ve yalnızca tek bir blok için yeterlidir. Split-K GEMM çekirdekleri düşük belirteç toplu iş boyutları için SM kullanımını artırabilse de, önbellekler ve matris çarpım birimleri genellikle yetersiz kullanılmaya devam eder.
Birlikte karıştırıldığında, ön doldurma istekleri içeren toplu işler ileri geçişte daha yüksek gecikmelere neden olur ve tüm örneğin kod çözme verimliliğini olumsuz etkiler. Ön doldurma isteklerini kod çözme istekleriyle karıştırmak veya parçalı ön doldurma kullanmak kod çözme performansını biraz artırabilse de, kod çözme verimliliğini en üst düzeye çıkarmak için bir örnek üzerinde yeterli isteği işleyecek ön doldurma verimliliğini korumak zordur. Tipik çıkış uzunluklarına sahip büyük modeller durumunda, kod çözme için büyük bir toplu iş boyutunu korumak amacıyla, ön doldurmanın ortalama gecikmeyi önemli ölçüde düşürecek ve çıkışta takılmalara neden olacak kadar sık gerçekleştirilmesi gerekir.
Bu sorunlar, ön doldurma ve kod çözme işlemlerini gerçekleştirmek için ayrı bir düğüm kümesi kullanılarak çözülebilir. Bir ön doldurma düğümü birden fazla kod çözücü düğümüyle ilişkilendirilerek, verimliliği en üst düzeye çıkarmak ve kod çözücü düğümlerinde kod çözme verimliliğini de en üst düzeye çıkarmak için eşzamanlı isteklerin yeterince büyük bir sayısını korumak amacıyla yeterli istek zamanlanabilir. Ön doldurma düğümleri KV önbelleklerini doldurur ve bunlar daha sonra kod çözücü düğümlerine aktarılır. Kod çözücüler ön doldurma için durmak zorunda kalmadığından, aktif isteklerin büyüyen kv_len değerinin genel etkisi çok daha az belirgin olduğundan gecikmeler çok daha belirgin hale gelir. Bunun bedeli, ağ üzerinden KV önbelleklerinin aktarımı onlarca ila yüzlerce milisaniye sürebileceğinden, İlk Belirteç Süresinde (TTFT) bir artış olarak ödenir.
KV Messenger
Perplexity\'de, ayrıştırılmış ön doldurma ve kod çözme uygulamamız, bir ağ üzerinden KV önbellek aktarımlarını ön doldurma düğümlerinden kod çözücü düğümlerine koordine etmek için LLM motoruyla etkileşime giren bir KV messenger etrafında oluşturulmuştur. Ön doldurma tarafında messenger, kod çözücü düğümlerinden gelen istekleri kabul eder, bunları toplu iş zamanlayıcısına devreder ve KV önbelleklerini mümkün olan en az gecikmeyle göndermek için ileri geçiş yürütmesini takip eder. Kod çözücü tarafında ise, çıkarılamaz sayfalar ayrıldıktan sonra messenger, KV önbelleği ve kod çözücü bağlam aktarımlarının tamamlandığı bildirilene kadar isteğin kod çözme için zamanlanmasını engeller.
Ön doldurmanın ayrıştırılması yüksek verimlilik, düşük gecikmeli bağlantılar gerektirir; bu nedenle uygulamamız, hem EFA hem de ConnectX Ağ Arayüz Denetleyicilerini (NIC) destekleyen RDMA için özel olarak tasarlanmıştır. KV Messenger, Uzak Doğrudan Bellek Erişimi (RDMA) ilkelleri üzerinde daha üst düzey düşük gecikmeli soyutlamalar sağlamak için fabric-lib sarmalayıcılarımızı kullanarak libfabric üzerine kurulmuştur ve verimli sayfa ile meta veri aktarımlarının yanı sıra düşük gecikmeli sinyalleşme uygular. Arka planda fabric-lib, verileri ön doldurma düğümünden kod çözücü düğümüne kopyalamak için bir GPU\'yu ve doğrudan bağlı NIC\'lerini koordine eder.
Alındığında, ön doldurma düğümü ilgili bir kaynak KV sayfa kümesi ayarlar ve yerel motorunu kullanarak istek için ön doldurmayı zamanlar. Gecikmeyi en aza indirmek için aktarımlar ileri geçişin bitmesini beklemez: bunun yerine KV sayfa kopyalamaları, model bireysel katmanlar için KV önbellek girdilerini KV önbelleğine eklemeyi bitirir bitirmez başlatılır. Ön doldurma istekleri parçalanabileceğinden, toplu iş zamanlayıcı yürütmeden önce o anda zamanlanan parçalar hakkında KV messenger\'ı bilgilendirir. Katmanları takip edebilmekle birlikte CUDA grafiklerini desteklemek için messenger, dikkatin çıkış projeksiyonundan sonra artırılan bir sayacı yoklayan özel bir konu (thread) tutar. Sayaç, paylaştırılmış bir ortamda yalnızca lider düğümde tutulur: KV önbellek girdileri eklemeden sonra ve dikkatten önce geçerli olmasına rağmen, çıkış projeksiyonu sıralar arasında indirgenerek bunları örtük olarak senkronize eder. Sayaçta bir değişiklik gözlemlendiğinde messenger bilgilendirilir ve bir katmanın aktarımını başlatmak için fabric-lib\'i çağırır.
Son parçanın aktarımı tamamlandıktan sonra, varsa ek meta veriler de kopyalanır: spekülatif kod çözme veya MTP, mantıksal değerlerin (logits) ve gizli durumların kod çözücüye taşınmasını gerektirir. Bu kopyalamalar da önceden ayrılmış tamponlara ve bu tamponlardan RDMA aracılığıyla gerçekleştirilir.
Son parçaya ait beklemedeki tüm aktarımlar tamamlandığında, ön doldurma düğümü KV sayfalarını serbest bırakır ve isteği tamamlar. Kod çözücü düğümüne açıkça bildirim yapılmaz; bunun yerine, tamamlanan işlem sayısını takip etmek için anlık sayaçlar kullanılır. Ön doldurma tarafındaki RDMA işlemlerinin sayısı, aktarılan sayfa sayısı ile orantılıdır. Bilinen sayfa ve bağlam kopyası sayısının tamamlanmasının ardından fabric-lib, bir isteğin kod çözmeye hazır olduğunu belirtmek için KV messenger\'ı çağırır. Messenger tüm bağlamı serbest bırakır ve isteği LLM motoruna devreder.
Paylaştırılmış KV Önbellek Aktarımları
Ön doldurucu ve kod çözücü Tensör Paralelliğine (TP) güveniyorsa ve KV önbelleklerini özdeş bir şekilde paylaştırıyor veya çoğaltıyorsa, tek bir aktarım motoru, tüm çoğaltmaların sayfalarını göndermek ve almak için birden fazla cihazı koordine eder. Modelin yürütücüsü birden fazla cihaz ve süreç arasında çoğaltılmış olmasına rağmen tek bir messenger ve aktarım motoru kullanabilmek için, KV önbelleklerini destekleyen cihaz belleğini ayırmak ve bunu ana sürece eşlemek amacıyla cuMem ve cuMemImportFromShareableHandle kullanılır. Aktarım motoru, KV önbellek dilimlerinin her birinin aktarımı için kullanılacak en yakın NUMA düğümündeki NIC\'leri ve CPU\'ları bulmak üzere düğümün topolojisini inceler.
Kaynak ve hedef özdeş bir şekilde paylaştırılmışsa, kaynak ve hedef cihazları ile sayfaları arasında bire bir eşleme olduğundan aktarımlar basittir. Bu durumda, paylaştırma aktarım gecikmelerine örtük olarak yardımcı olur: daha fazla GPU kullanılarak daha fazla ilişkili NIC istihdam edilebilir ve tam bant genişliği kullanımına daha yakın bir sonuca ulaşılabilir. Ancak bir uyumsuzluk varsa, aktarım motoru kaynak ve hedef dilimler arasındaki orana bağlı olarak sayfaları bölmeli veya yeniden oluşturmalıdır.
Ön doldurucu KV önbelleğini daha fazla cihaz arasında bölerse, tam sayfalar karşılık gelen yarıların ön doldurucu cihazlardan dışarı gönderilmesiyle kod çözücü üzerinde yeniden oluşturulur. Kod çözücünün daha fazla parçası varsa, birden fazla kaynaktan sayfa alır. Kod çözücünün alması beklenen RDMA yazma sayısını hesaplayabilmesi için ön doldurucunun paylaştırma şemasını bilmesi gerekir. Çoğaltma söz konusu olduğunda, ön doldurucu cihazları kendi içlerinde tam KV önbelleğini çoğaltan çoğaltma kümelerine ayırır. Hedef çoğaltma kümelerine, RDMA yazmalarını başlatmak için mevcut tüm cihazları kullanmak üzere kaynak kümelerden biri rastgele atanır.
Paylaştırılmış aktarımlar, KV önbelleklerinde küçük bir değişiklik yapılmasını gerektirir. Varsayılan olarak FlashInfer, bir sayfa içindeki belirteçleri kafalar (heads) içinde sıralayan NHD düzenine güvenir. Önbellekler büyük olasılıkla dikkat kafalarının sayısı boyunca paylaştırıldığından, bu durum kafa içinde süreksizlik yaratır. RDMA aktarımları aralıklı (strided) yazmaları örtük olarak desteklemez ve aktarımı gerçekleştirmek için her kafa başına bir işlem gerektirir. Bunun yerine, libfabric ile etkileşim sayısını azaltmak amacıyla KV önbelleklerini, kafa boyutunu belirteç sayısının önüne koyan HND düzenini kullanarak düzenleriz. Bu, sürekliliği sağlayarak bir sayfanın tek bir yazma işlemiyle kopyalanmasına olanak tanır.
Spekülatif Kod Çözme
Spekülatif kod çözme, ayrıştırılmış ön doldurma-kod çözme sürecinde küçük değişiklikler gerektirir. Uygulamamızda, ön doldurma düğümlerinin belirteç örneklemesi yapmasına izin verilmez. Perplexity\'nin Sonar modelleri yapılandırılmış çıktıyı desteklediğinden, şema işlemci uygulamalarını ön doldurucular ile kod çözücüler arasında senkronize etme karmaşıklığına girmek istemiyoruz. MTP ve spekülatif kod çözme mekanizmalarında, taslak modeli son belirtece kadar ön doldurmak, hedef modelden belirteç örneklemeyi içerir.
Bu sorunları aşmak için ön doldurma, girdi dizisinin son belirtecini içermez. Bunun yerine, son belirteçten önceki ön doldurmadan elde edilen gizli durumlar veya mantıksal değerler aktarılır ve kod çözücü üzerindeki sonraki adımda bir kod çözme belirteci olarak ele alınır. İlk belirteci yayınlamak için ön doldurmadan sonra tam bir kod çözme adımının gerçekleştirilmesi gerekeceğinden bu durum gecikmeleri biraz artırsa da, uygulamanın karmaşıklığı büyük ölçüde azalır.
Ayrıştırılmış Dağıtımlar
Üretim trafiğini veya şirket içi değerlendirme iş yüklerini desteklemek için farklı modellerle birden fazla ayrıştırılmış yapılandırma dağıttık veya bunları denedik. Modellerin boyutuna ve dikkat mekanizmasına dayanarak, GPU\'ları en iyi şekilde kullanmak için ön doldurucu ve kod çözücü düğümleri için uygun paylaştırma şemaları seçtik.
DeepSeek-R1
DeepSeek ile hem Tensör Paraleli (TP) hem de Veri Paraleli (DP) dağıtımlarını değerlendirdik. Önceki blog yazı ۱۹atışlarında tartışıldığı gibi, TP dağıtımları daha iyi gecikme sağlar ve yoğun trafiğe hizmet etmek için daha fazla GPU gerektiren daha düşük verimlilik maliyetine sahiptir. DP dağıtımları ise yük ile çok daha iyi ölçeklenir, ancak cihazlar arası veya düğümler arası iletişim maliyeti nedeniyle tepe verimlilikleri daha düşüktür.
DeepSeek, KV önbelleklerini sıkıştıran Çok Kafalı Gizli Dikkat (Multi-Head Latent Attention) mekanizmasına güvenir. Tüm KV kafaları tek bir gizli vektörde sıkıştırıldığından, TP, gizli vektörleri tüm sıralarda çoğaltmak zorunda olduğundan KV önbelleklerini paylaştıramaz. Her sıra aynı gizli temsilden farklı kafalar çıkarabildiği için paylaştırma, sıkıştırmadan sonra gerçekleşir. Sonuç olarak, tüm KV önbellek parçaları hem ön doldurucu hem de kod çözücü parçalarında özdeştir.
Düğüm içi TP kurulumu ile hem ön doldurucular hem de kod çözücüler özdeş bir şekilde paylaştırılır. Tüm kullanılabilir NIC\'leri tam olarak kullanmak için aktarımlar tüm sıralardan gönderilir. Ancak TP sıra boyutunun daha küçük olduğu veya her DP sıra boyutunun tek bir GPU\'ya atandığı bir DP dağıtımında, KV önbelleğinin çoğaltılmış bir kopyasını tutan herhangi bir ön doldurucu cihaz bunu gönderebilir. İstekleri tüm kullanılabilir NIC\'ler arasında dengelemek için, ön doldurucudan kod çözücüye KV önbelleğini göndermek üzere rastgele bir GPU ve bir NIC seçeriz.
Karışık ön doldurma-kod çözme ile R1 dağıtımımız, yüzlerce milisaniyelik mertebedeki sık ön doldurma kesintileri nedeniyle tutarlı bir şekilde 50 TPS\'yi aşmakta zorlanıyordu. Buna karşılık, ön doldurmayı ayırarak her istek için TTFT\'ye yaklaşık 100 ms\'lik bir ceza ekledik, ancak tek bir ön doldurucu düğüm 3 kod çözücü düğümünde tutarlı toplu iş boyutlarını koruyabildi ve kod çözücü düğümü başına yaklaşık 1 QPS yükü işlerken 90 TPS\'yi aşan bir verimlilik sağladı. Veri paraleli dağıtımlarda TPS, 8 sıra tek bir düğüme olacak şekilde sıra başına 1 QPS yükü işleyebilen örneklerle birlikte yaklaşık 50 ile biraz daha düşüktü.
Qwen3-Coder
Bu 480B model, Gruplanmış Sorgu Dikkati (GQA) kullanır; böylece dikkat kolayca paylaştırılabilir ve KV önbellekleri için bellekten ödün vermeden tensör paralelliğinden yararlanabilir. Sonuç olarak, modeli hem ön doldurma hem de kod çözme için 8 GPU arasında paylaştırabilir, yaklaşık 3 kod çözücü düğümünü tek bir ön doldurucu düğümle eşleştirebiliriz. Dikkat paylaştırıldığı için, ön doldurucu ve kod çözücü KV önbelleklerini paylaştırmak amacıyla HND KV önbellek düzenine güveniriz; ön doldurucu sıralarını kod çözücü sıralarıyla eşleştirir ve dilimleri paralel olarak aktarmak için tüm NIC\'leri tam olarak kullanırız.
Alıntı
@online{perplexity2025DisaggregatedPrefillAndDecode,
author = {{Perplexity} and Nandor Licker and Lequn Chen and Kevin Hu and Erin McKnight and Phi Hoang},
title = {{Disaggregated prefill and decode}},
date = {2025-08-01},
url = {https://perplexity.coolkeypoint.com/hub/blog/disaggregated-prefill-and-decode}
}