Transformers documentation

Arsitektur pengelompokan berkelanjutan

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

and get access to the augmented documentation experience

to get started

Arsitektur pengelompokan berkelanjutan

Batching tradisional memproses sekelompok permintaan tetap secara bersamaan dan menunggu hingga permintaan yang paling lambat selesai sebelum memulai kelompok berikutnya. Hal ini menyebabkan GPU menganggur di antara batch.

Dengan continuous batching, pada setiap langkah generasi, scheduler memeriksa permintaan yang telah selesai dan segera menggantinya dengan permintaan yang sedang menunggu. Permintaan pendek dikeluarkan begitu selesai, dan GPU tetap sibuk sepanjang waktu. Hasilnya adalah throughput yang jauh lebih tinggi dan latensi rata-rata yang lebih rendah.

Siklus hidup permintaan

Sebuah permintaan melewati empat status dari pengiriman hingga penyelesaian.

       WAIT IN QUEUE          LOAD PROMPT INTO KV       STREAM OUTPUT           DONE
    ┌────────────────┐    ┌─────────────────────┐    ┌─────────────────┐    ┌────────┐
    │    PENDING     │───▶│     PREFILLING      │───▶│    DECODING     │───▶│FINISHED│
    │  ○ ○ ○ · · ·   │    │ ████████░░░░░░░░░░  │    │  → → → → · ·    │    │   ✓    │
    └────────────────┘    └─────────────────────┘    └─────────────────┘    └────────┘
         others ahead            prompt chunks              +1 token/step
  1. Pending — permintaan berada dalam antrean dan menunggu untuk dijadwalkan.
  2. Prefilling — token prompt diproses dalam satu langkah maju (forward pass).
  3. Decoding — token output dihasilkan satu per satu.
  4. Selesai — generasi telah selesai dan hasilnya tersedia.

Sebuah permintaan dipindahkan dari status pending ke prefilling ketika scheduler menemukan anggaran token dan ruang cache yang cukup untuk menerima permintaan tersebut. Jika prompt terlalu panjang untuk muat dalam satu langkah, scheduler membaginya ke beberapa forward pass (chunk prefill).

Chunked prefill

Memproses prompt panjang dalam satu langkah sangat mahal karena menghalangi permintaan lain untuk menghasilkan. Chunked prefill menyelesaikan masalah ini dengan membagi prefill besar ke dalam beberapa langkah.

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 ·

Ketika prompt dari permintaan baru melebihi anggaran token yang tersedia (yang diatur oleh max_batch_tokens di ContinuousBatchingConfig), penjadwal memproses sebanyak mungkin token dan menahan sisanya. Pada langkah-langkah berikutnya, ia melanjutkan dari titik di mana ia berhenti, menyisipkan kerja prefill dengan langkah decode yang sedang berlangsung. Hal ini menjaga agar batch tetap produktif dan mengurangi waktu hingga token pertama untuk permintaan lainnya.

Penjadwal

Penjadwal menentukan permintaan mana yang bergabung ke setiap langkah maju berdasarkan dua anggaran dan batas permintaan.

  • Anggaran token — jumlah maksimum token kueri yang diproses dalam satu langkah maju (forward pass), diatur oleh max_batch_tokens di ContinuousBatchingConfig.
  • Anggaran cache — jumlah total halaman KV yang dapat dibaca dalam satu kali proses, yang dibatasi oleh ukuran total cache.
  • Batas permintaan — jumlah maksimum permintaan dalam satu langkah maju (forward pass), yang diatur oleh max_requests_per_batch. Tensor logits berskala dengan ukuran kosakata, sehingga batas ini membatasi memori puncak untuk model dengan kosakata besar.

FIFO

FIFOScheduler adalah penjadwal default. Penjadwal ini mengisi batch berdasarkan urutan prioritas. Permintaan decode aktif diproses terlebih dahulu, kemudian permintaan prefill aktif, dan kemudian permintaan baru yang menunggu berdasarkan urutan kedatangan. Hal ini memaksimalkan throughput dengan mendorong permintaan yang sudah ada untuk diselesaikan sebelum menarik permintaan baru.

PrefillFirst

PrefillFirstScheduler menyelesaikan operasi prefill berchunk sebelum melanjutkan decode. Hal ini mengurangi fragmentasi untuk beban kerja yang didominasi oleh prompt panjang.

Atur scheduler di ContinuousBatchingConfig.

cb_config = ContinuousBatchingConfig(scheduler_type="prefill_first")

Manajemen memori

KV cache dipaging sehingga permintaan dengan panjang berbeda dapat berbagi pool memori tetap tanpa fragmentasi. Pool memori bersama, diimplementasikan sebagai CachePool, menyediakan chunk tersebut ke setiap allocator tipe perhatian, yang membaginya menjadi blok dan halaman.

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

Continuous batching hanya mendukung dua jenis perhatian: full_attention (FullAttentionCacheAllocator) dan sliding_attention (SlidingAttentionCacheAllocator). Model hibrida mendapatkan satu allocator untuk masing-masing jenis. Jenis perhatian berasal dari model (layer_types / sliding_window), bukan dari ContinuousBatchingConfig.

Lapisan geser hanya mempertahankan jendela tetap dari token-token terbaru dengan mendaur ulang seperangkat kecil halaman. Lapisan ini tidak mendukung tabel blok kernel atau berbagi blok.

Cache ini menyisakan dua chunk di depan chunk data untuk padding baca dan tulis yang tidak pernah digunakan oleh permintaan. Chunk tersebut tidak pernah dialokasikan ke permintaan.

Penentuan ukuran cache

Manajer menyimpulkan berapa banyak chunk yang muat saat startup dari memori GPU yang tersedia. Ia memperhitungkan tensor KV, mask perhatian, aktivasi, dan indeks pencatatan, lalu mengatur ukuran pool agar muat di dalam max_memory_percent dari memori yang tersedia. Jika tidak diatur, nilainya secara default mengikuti nilai yang sesuai dengan perangkat keras dan parameter continuous batching. MPS menyisakan ruang yang lebih sedikit untuk cache, di mana CPU dan GPU berbagi memori, untuk menghindari swapping. num_blocks mengekspresikan kapasitas sebagai blok model utuh berisi page_size token ketika Anda menyetelnya secara eksplisit.

Anda dapat mengunci nilai di ContinuousBatchingConfig.

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

Penerimaan

Sebelum sebuah permintaan bergabung ke dalam batch, scheduler memeriksa apakah kapasitas bebas yang cukup tersedia untuk setiap allocator tipe perhatian. Jika ada allocator yang kekurangan kapasitas, permintaan tersebut ditolak dan tidak ada alokasi yang dilakukan.

Untuk menghindari pengisian cache hingga offloading menjadi satu-satunya opsi, scheduler menerapkan safety_margin. Begitu kapasitas cache bebas turun di bawah safety_margin, prefill baru ditahan dan hanya decode aktif yang dilanjutkan. Scheduler FIFO secara default menggunakan 0.15 (15% kapasitas ditahan sebagai cadangan), sedangkan prefill_first secara default menggunakan 0.0. Atur safety_margin di ContinuousBatchingConfig untuk menyesuaikannya, di mana 0 menonaktifkan margin tersebut.

Penyimpanan sementara awalan

Ketika dua permintaan berbagi prefiks prompt, mereka dapat berbagi blok yang menyimpan KV untuk prefiks tersebut. Setiap blok yang selesai di-hash berdasarkan kontennya. Permintaan berikutnya dengan prefiks yang cocok akan menggunakan ulang blok tersebut dan melewati prefill untuk token-token tersebut. Blok yang dibagikan dihitung referensinya dan hanya dikembalikan ke pool bebas setelah semua permintaan yang menggunakannya selesai.

allow_block_sharing secara default bernilai True. Prefix sharing hanya aktif ketika setiap allocator mendukung sharing, sehingga hanya model full-attention-only yang menggunakannya. Model sliding murni dan hybrid menonaktifkannya. Atur allow_block_sharing=False untuk workload dengan prompt pendek dan generasi panjang, di mana overhead bookkeeping melebihi penghematannya.

Eviction

Blok yang dibebaskan tidak dihapus dan tetap berstatus “terinisialisasi” dengan konten dan hash-nya utuh sehingga pencocokan prefiks di kemudian hari dapat memakainya kembali. Ketika pool yang belum terinisialisasi menipis, manajer secara malas menurunkan blok terinisialisasi terbaru kembali menjadi belum terinisialisasi, dengan membuang hash-nya. Cache prefiks adalah lapisan best-effort yang dibuang oleh allocator untuk terus melayani permintaan baru.

Reset lunak

Soft reset adalah jalur cadangan untuk offloading. Ketika manajer tidak dapat menyalin KV cache dari sebuah permintaan ke pool swap CPU, karena CPU offloading dinonaktifkan atau pool sudah penuh, manajer akan melakukan soft reset pada permintaan tersebut. Proses ini menambahkan token yang telah dihasilkan oleh permintaan ke prompt aslinya, membebaskan blok cache-nya, dan mengembalikannya ke antrean. Bagian Offloading membahas permintaan mana yang dipilih.

Ketika cache memiliki ruang lagi, permintaan dilanjutkan dari titik di mana ia terhenti dengan riwayat generasinya yang dikodekan dalam prompt.

Graf CUDA

Setiap langkah generasi melibatkan overhead CPU, mulai dari perakitan batch hingga pengiriman kernel GPU dan pembacaan hasilnya. CUDA graphs menghilangkan overhead ini dengan merekam urutan eksekusi GPU secara penuh sekali dan memutar ulang untuk batch dengan bentuk yang cocok.

Karena bentuk batch berubah setiap langkah, sistem continuous batching menangani hal ini dengan padding dan caching.

  1. Panjang query dipadatkan ke kelipatan terdekat dari q_padding_interval_size.
  2. Panjang KV di-*padding* hingga kelipatan terdekat dari kv_padding_interval_size.
  3. Graf yang direkam disimpan dalam cache.

Ketika bentuk bantalan (padded shape) dari sebuah batch cocok dengan grafik yang di-cache, grafik tersebut diputar ulang tanpa dispatch CPU apa pun. Bentuk baru memicu penangkapan (capture) grafik baru.

Graf CUDA memerlukan alur kontrol statis dan tidak kompatibel dengan masker perhatian. Graf ini dideteksi secara otomatis secara bawaan dan dinonaktifkan ketika kondisi tidak terpenuhi.

Batching asinkron

Async batching menggunakan dua pasangan buffer I/O dan dua stream CUDA untuk menumpuk pekerjaan CPU dan GPU.

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] ──

Saat GPU menghitung batch N, CPU menyiapkan batch N+1. Namun, menumpuk keduanya membutuhkan sekitar dua kali lipat VRAM.

Async batching memerlukan CUDA graphs untuk aktif, karena replay graph menyediakan alamat tensor yang stabil yang diperlukan agar stream overlap menjadi benar.

Offloading

Permintaan yang menghasilkan output panjang dapat terus bertambah hingga KV cache tidak memiliki ruang lagi untuk mengalokasikan blok berikutnya. Alih-alih crash, manager meng-offload permintaan aktif kembali ke antrean tunggu untuk membebaskan blok bagi permintaan yang tersisa.

Offloading bersifat didorong oleh permintaan. Penjadwal melaporkan permintaan stalled, yaitu permintaan aktif yang gagal mengalokasikan blok yang dibutuhkan, beserta jumlah blok yang diminta masing-masing. Manajer melakukan offloading pada jumlah permintaan terkecil yang diperlukan untuk membebaskan blok yang cukup guna memenuhi permintaan tersebut. Offloading dilakukan dari yang terbaru terlebih dahulu, sehingga permintaan yang sudah dijadwalkan ke batch saat ini tetap mempertahankan cache-nya, dan permintaan aktif terakhir yang tersisa tidak pernah di-offload.

Dua situasi memicu offloading.

  • Sebuah batch dijadwalkan, tetapi beberapa permintaan aktif terhenti. Manajer memindahkan cukup banyak permintaan yang terhenti sehingga batch berikutnya dapat mengalokasikan blok untuk sisanya.
  • Cache penuh dan tidak ada batch yang dapat dijadwalkan sama sekali. Manager melakukan offload permintaan dan mencoba penjadwalan ulang dalam loop di dalam langkah yang sama, hingga batch muat atau tidak ada permintaan yang dapat di-offload. Ia tidak lagi menunggu forward pass berikutnya untuk pulih.

Permintaan yang di-offload mengikuti salah satu dari dua jalur. Ketika cpu_offload_space lebih besar dari 0.0, manajer menyalin blok KV cache permintaan ke buffer CPU pinned yang telah dialokasikan sebelumnya, dengan mem-batch setiap permintaan yang muat di pool swap ke dalam satu transfer. Setelah ruang cache GPU tersedia, manajer menyalin blok-blok tersebut kembali ke GPU dan melanjutkan permintaan tanpa menghitung ulang prompt dan token yang telah dihasilkan. Permintaan yang tidak muat di pool, atau setiap permintaan yang di-offload ketika CPU offloading dinonaktifkan, kembali ke soft reset.

Langkah selanjutnya

Update on GitHub