Transformers documentation

Sürekli toplu işleme mimarisi

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v5.22.1).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Sürekli toplu işleme mimarisi

Geleneksel toplu işleme (batching), sabit bir istek grubunu birlikte işler ve bir sonraki gruba başlamadan önce en yavaş olanın tamamlanmasını bekler. Bu durum, toplu işlemler arasında GPU'nun boşta kalmasına neden olur.

Sürekli toplu işleme (continuous batching) ile her üretim adımında zamanlayıcı, tamamlanmış istekleri kontrol eder ve bunları bekleyen isteklerle anında değiştirir. Kısa istekler biter bitmez dışarı atılır ve GPU tüm süre boyunca meşgul kalır. Sonuç olarak, önemli ölçüde daha yüksek bir aktarım hızı ve daha düşük ortalama gecikme elde edilir.

İstek yaşam döngüsü

Bir istek, gönderimden tamamlanmaya kadar dört durumdan geçer.

       WAIT IN QUEUE          LOAD PROMPT INTO KV       STREAM OUTPUT           DONE
    ┌────────────────┐    ┌─────────────────────┐    ┌─────────────────┐    ┌────────┐
    │    PENDING     │───▶│     PREFILLING      │───▶│    DECODING     │───▶│FINISHED│
    │  ○ ○ ○ · · ·   │    │ ████████░░░░░░░░░░  │    │  → → → → · ·    │    │   ✓    │
    └────────────────┘    └─────────────────────┘    └─────────────────┘    └────────┘
         others ahead            prompt chunks              +1 token/step
  1. Beklemede — istek kuyruğa alındı ve zamanlamaya alınmayı bekliyor.
  2. Prefilling — prompt jetonları bir ileri geçişte işlenir.
  3. Çözme — çıktı jetonları tek tek üretilir.
  4. Tamamlandı — üretim tamamlandı ve sonuç kullanılabilir durumda.

Bir istek, zamanlayıcı istek için yeterli token bütçesi ve önbellek alanı bulduğunda bekleyen durumundan ön doldurma (prefilling) durumuna taşınır. İstek metni (prompt) tek bir adıma sığmayacak kadar uzunsa, zamanlayıcı bunu birden fazla ileri geçişe (forward pass) böler (parça ön doldurma / chunk prefill).

Parçalı ön işleme

Tek adımda uzun bir istem (prompt) işlenmesi, diğer isteklerin üretimi engellendiği için maliyetlidir. Chunked prefill, büyük prefill'leri birden fazla adıma bölerek bu sorunu çözer.

Blocking (long prefill blocks the batch)
  Step:   1        2        3        4
  Req A  [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓]   ← one huge prefill
  Req B  ················ wait ················
  Req C  ················ wait ················

Chunked (same prompt split and interleave with decode)
  Step:   1          2          3          4
  Req A  [▓▓ pre]    [▓▓ pre]   [▓▓ pre]   [→ dec]
  Req B  [→ dec]     [→ dec]    [▓▓ pre]   [→ dec]
  Req C  [→ dec]     · idle ·   [→ dec]    · idle ·

Yeni bir isteğin prompt'u kullanılabilir token bütçesini (ContinuousBatchingConfig içindeki max_batch_tokens ile belirlenen) aştığında, zamanlayıcı mümkün olan en fazla token'ı işler ve kalanı bekletir. Sonraki adımlarda, kaldığı yerden devam eder ve prefill işlemlerini devam eden decode adımlarıyla iç içe geçirir. Bu, batch'in verimli kalmasını sağlar ve diğer istekler için ilk token'a ulaşma süresini kısaltır.

Zamanlayıcı

Zamanlayıcı, her ileri geçişe hangi isteklerin katılacağını iki bütçe ve bir istek sınırı temelinde belirler.

  • Token bütçesi — ContinuousBatchingConfig içindeki max_batch_tokens ile belirlenen, tek bir ileri geçişte işlenen sorgu tokenlerinin maksimum sayısı.
  • Önbellek bütçesi — tek bir geçişte okunabilecek KV sayfalarının toplam sayısı; bu değer, toplam önbellek boyutuyla sınırlıdır.
  • İstek sınırı — max_requests_per_batch ile belirlenen, tek bir ileri geçişteki (forward pass) maksimum istek sayısı. Logitler tensörü sözlük boyutuyla orantılı olarak büyür; bu nedenle bu sınır, büyük sözlüklü modellerde tepe bellek kullanımını sınırlar.

FIFO

FIFOScheduler varsayılan zamanlayıcıdır. Toplu işlemi öncelik sırasına göre doldurur. Önce etkin decode istekleri, ardından etkin prefill istekleri ve en son da varış sırasına göre yeni bekleyen istekler işlenir. Bu, yeni istekleri çekmeden önce mevcut istekleri sonuna kadar işleyerek verimliliği en üst düzeye çıkarır.

PrefillFirst

PrefillFirstScheduler, decode işlemlerine devam etmeden önce parçalı prefill işlemlerini tamamlar. Bu, uzun istemler tarafından baskılanan iş yükleri için parçalanmayı azaltır.

Zamanlayıcıyı ContinuousBatchingConfig içinde ayarlayın.

cb_config = ContinuousBatchingConfig(scheduler_type="prefill_first")

Bellek yönetimi

KV önbelleği sayfalıdır, böylece farklı uzunluklardaki istekler parçalanma olmadan sabit bir bellek havuzunu paylaşabilir. CachePool olarak uygulanan paylaşımlı bir bellek parçaları havuzu, bu parçaları her dikkat türü ayıracına sağlar; ayıracılar da parçaları bloklara ve sayfalara böler.

Shared pool (first two chunks reserved for padding / never allocated)

  chunk N  [ ======================================== ]
               |                            |
    full attn: [ FULL BLOCK 0 | FULL BLOCK 1 | … ]
               [ page L0 | page L0 | … ]   (one page per full layer)

    sliding:   [ ----------- SLIDING BLOCK 0 ----------- ]
               [ page L1 | page L2 | page L3 | … ]  (one page per sliding layer)

  page  = page_size tokens of KV for one layer
  block = one page per layer of that attention type
  chunk = pool unit sized so full and sliding blocks tile the same bytes

Sürekli toplu işleme (continuous batching) yalnızca iki dikkat türünü destekler: full_attention (FullAttentionCacheAllocator) ve sliding_attention (SlidingAttentionCacheAllocator). Hibrit modellerin her türden birer tahsis edicisi (allocator) olur. Dikkat türü, ContinuousBatchingConfig değil, modelden (layer_types / sliding_window) gelir.

Sliding layers, küçük bir sayfa setini geri dönüştürerek yalnızca son tokenların sabit bir penceresini tutar. Çekirdek blok tablolarını veya blok paylaşımını desteklemezler.

Önbellek, isteklerin asla kullanmadığı dolgu okuma ve yazmaları için veri parçalarının iki parça öncesinde yer ayırır. Bu parçalar asla isteklere tahsis edilmez.

Önbellek boyutlandırma

Yönetici, başlangıçta boş GPU belleğinden kaç parçanın sığacağını çıkarır. KV tensörlerini, dikkat maskelerini, aktivasyonları ve kayıt indekslerini hesaba katar, ardından havuzu, kullanılabilir belleğin max_memory_percent oranına sığacak şekilde boyutlandırır. Ayarlanmadığında, donanıma ve sürekli toplama (continuous batching) parametrelerine göre bir varsayılan değere geçer. CPU ve GPU'nun belleği paylaştığı MPS'te, takas (swapping) yaşanmaması için önbelleğe daha az alan ayrılır. num_blocks, açıkça ayarlandığında kapasiteyi, page_size token'lık tam model blokları olarak ifade eder.

ContinuousBatchingConfig içindeki değerleri sabitleyebilirsiniz.

cb_config = ContinuousBatchingConfig(
    page_size=256,
    num_blocks=4096,
    max_batch_tokens=512,
    max_memory_percent=0.8,
)

Kabul

Bir istek partiye katılmadan önce, zamanlayıcı her dikkat türü ayıracı için yeterli boş kapasite olup olmadığını kontrol eder. Herhangi bir ayıracı yetersiz kalacaksa, istek reddedilir ve hiçbir şey ayrılmaz.

Önbelleğin offloading tek seçenek olana kadar dolmasını önlemek için zamanlayıcı safety_margin uygular. Boş önbellek kapasitesi safety_margin altına düştüğünde, yeni prefill işlemleri bekletilir ve yalnızca aktif decode işlemleri devam eder. FIFO zamanlayıcısının varsayılan değeri 0.15'tir (kapasitenin %15'i yedekte tutulur), prefill_first'ün varsayılan değeri ise 0.0'dır. Ayarlamak için safety_margin değerini ContinuousBatchingConfig içinde belirleyin; 0 değeri marjı devre dışı bırakır.

Önek önbellekleme

İki istek bir prompt önekinini paylaşıyorsa, bu önekin KV'sini tutan blokları da paylaşabilir. Her tamamlanan blok içerik-karma (content-hash) ile işaretlenir. Eşleşen bir önekiye sahip sonraki bir istek, bloğu yeniden kullanır ve bu token'lar için prefill adımını atlar. Paylaşılan bloklar referans sayacıyla izlenir ve onları kullanan tüm istekler tamamlanana kadar serbest havuza geri dönmez.

allow_block_sharing varsayılan olarak True'dir. Önek paylaşımı, yalnızca tüm tahsisçiler paylaşımı desteklediğinde etkin olur; bu nedenle yalnızca tam dikkat (full-attention) kullanan modeller bunu kullanır. Saf kayan pencere (sliding) ve hibrit modeller bunu devre dışı bırakır. Kısa istemler ve uzun üretimler içeren iş yüklerinde, kayıt tutma maliyeti tasarrufu aştığında allow_block_sharing=False olarak ayarlayın.

Atma

Serbest bloklar silinmez ve içerikleri ile hash değerleri olduğu gibi korunarak “başlatılmış” durumda kalır; böylece sonraki bir önek eşleşmesi bunları yeniden kullanabilir. Başlatılmamış havuz azaldığında, yönetici en son başlatılmış bloğu tembelce başlatılmamış duruma düşürür ve hash değerini atar. Önek önbelleği, tahsisçinin yeni isteklere hizmet vermeye devam etmesi için atabileceği bir en iyi çaba katmanıdır.

Yumuşak sıfırlama

Yumuşak sıfırlama, offloading için yedek yoldur. Yönetici, CPU offloading devre dışı olduğunda veya havuz dolu olduğunda bir isteğin KV önbelleğini CPU takas havuzuna kopyalayamazsa, isteği yumuşak sıfırlar. İsteğin üretilen tokenlarını orijinal istemine ekler, önbellek bloklarını serbest bırakır ve isteği yeniden kuyruğa ekler. Hangi isteklerin seçildiği Offloading bölümünde açıklanır.

Önbellekte tekrar yer açıldığında, istek, üretim geçmişi prompt'a kodlanmış haliyle kaldığı yerden devam eder.

CUDA grafikleri

Her üretim adımı, partiyi birleştirmekten GPU çekirdeklerini dağıtmaya ve sonuçları okumaya kadar CPU yükü içerir. CUDA grafikleri, tam GPU yürütme dizisini bir kez kaydederek ve eşleşen şekillere sahip partiler için yeniden oynatarak bu yükü ortadan kaldırır.

Toplu işlem (batch) şekilleri her adımda değiştiği için, sürekli toplu işlem (continuous batching) sistemi bunu dolgu (padding) ve önbellekleme (caching) ile yönetir.

  1. Sorgu uzunlukları, q_padding_interval_size'in en yakın katına kadar doldurulur.
  2. KV uzunlukları, kv_padding_interval_size'in en yakın katına kadar doldurulur.
  3. Kaydedilen grafikler bir önbellekte saklanır.

Bir partinin dolgulu (padded) şekli önbellekteki bir graf ile eşleştiğinde, graf herhangi bir CPU dağıtımı (dispatch) olmadan yeniden oynatılır. Yeni şekiller yeni bir graf yakalama işlemi tetikler.

CUDA grafikleri statik kontrol akışı gerektirir ve dikkat maskeleriyle uyumlu değildir. Varsayılan olarak otomatik olarak algılanır ve koşullar sağlanmadığında devre dışı bırakılır.

Eşzamanlı toplu işleme

Async batching, CPU ve GPU işlemlerini üst üste bindirmek için iki I/O tampon çifti ve iki CUDA akışı kullanır.

Sequential
  CPU  ── [prep N] ·········idle········· [prep N+1] ·········idle·········
  GPU  ·········idle········· [compute N] ·········idle········· [compute N+1]

Async
  CPU  ── [prep N] [prep N+1] [prep N+2] ──
  GPU  ── ········ [compute N] [compute N+1] ──

GPU N. partiyi hesaplarken, CPU N+1. partiyi hazırlar. Ancak bu iki işlemi üst üste bindirmek, VRAM'in yaklaşık iki katını gerektirir.

Async batching, stream overlap'inin doğru olması için gereken kararlı tensor adreslerini sağlayan grafik yeniden oynatma gerektirdiğinden, CUDA grafiklerinin etkin olması gerekir.

Offloading

Uzun çıktılar üreten istekler, KV önbelleğinin bir sonraki bloğu ayırmak için boş alanı kalmayana kadar büyüyebilir. Çökme yerine, yönetici aktif istekleri kalan istekler için blokları boşaltmak üzere bekleme kuyruğuna geri taşır.

Offloading talep odaklıdır. Zamanlayıcı, stalled istekleri, yani ihtiyaç duydukları blokları tahsis edemeyen aktif istekleri ve her birinin kaç blok talep ettiğini raporlar. Yönetici, bu talebi karşılayacak kadar blok serbest bırakmak için gereken en az sayıda isteği offload eder. Önce en yenilerini offload eder; böylece mevcut batch'e zaten zamanlanmış istekler önbelleklerini korur ve son kalan aktif istek asla offload edilmez.

İki durum, offloading'i tetikler.

  • Bir parti planlanır, ancak bazı aktif istekler durur. Yönetici, sonraki partinin kalanlar için bloklar ayırabilmesi için duran isteklerin yeterli kısmını devre dışı bırakır.
  • Önbellek dolu ve hiçbir parti zamanlanamıyor. Yönetici, bir parti sığana veya hiçbir istek aktarılamayana kadar aynı adım içinde döngü halinde istekleri aktarıp zamanlamayı yeniden deniyor. Artık kurtulmak için bir sonraki ileri geçişin gelmesini beklemiyor.

Offload edilen bir istek iki yoldan birini izler. cpu_offload_space değeri 0.0 değerinden büyükse, yönetici istek KV önbellek bloklarını önceden ayrılmış sabitlenmiş CPU tamponuna kopyalar ve swap havuzuna sığan her isteği tek bir aktarım olarak toplar. GPU önbellek alanı kullanılabilir hale geldiğinde, yönetici blokları GPU'ya geri kopyalar ve istek, promptunu ve üretilen tokenlarını yeniden hesaplamadan devam eder. Havuza sığmayan istekler veya CPU offload devre dışı bırakıldığında tüm offload edilen istekler, yumuşak sıfırlamaya geri döner.

Sonraki adımlar

Update on GitHub