Lensa ML

Continuous Batching

Langkah 1 dari 6: Kenapa Harus Batch

Satu pembacaan weight melayani semua sequence

Ukuran batch (sequence yang di-decode bersama)8
Token output/s (GPU ilustratif: 300 TFLOP/s, 2 TB/s)02k4k6k8k116324864ukuran batch1,024 tok/slinear sempurnamodel chat 7B, fp16, 512 token di cache per sequence; waktu ilustratif
Throughput
1,024 tok/s
Waktu per langkah decode
7.8 ms
Percepatan vs batch 1
7.0×
Dibatasi oleh
memori

8 sequence berbagi satu pembacaan weight, jadi throughput-nya 7,0× batch 1, sementara tiap langkah melambat dari 6,9 ms ke 7,8 ms. Weight masih mencakup 86% byte yang dibaca.

Geser Ukuran batch dari 1 sampai 64 dan bandingkan kurvanya dengan garis putus-putus. Waktu per langkah di sini adalah max(FLOP ÷ 300 TFLOP/s, byte ÷ 2 TB/s), dan byte terdiri atas 13,5 GB weight ditambah cache milik tiap sequence: 512 token × 0,5 MiB, sekitar 0,27 GB per sequence. Weight dipakai bersama oleh seluruh batch, tetapi cache tidak, jadi kurvanya tetap di bawah garis linear. Server sungguhan menunjukkan bentuk yang sama, dan karena itulah kecepatan per pengguna dan total throughput dilaporkan terpisah.

Langkah 2 dari 6: Static Batching

Semua menunggu request terpanjang

Request dalam batch6
Satu baris per request, satu sel per langkah decode (panjang dengan seed tetap)04812162024R111R210R319R421R513R68batch selesai di langkah 21menghasilkan tokenselesai, slot menganggur
Langkah yang dijalankan batch
21
Slot-step berguna
82
Slot-step menganggur
44
Utilisasi
65%

R4 butuh 21 langkah, jadi semua request lain menahan slot-nya sampai saat itu. 44 dari 126 slot-step (35%) tidak menghitung apa pun, dan request yang mengantre menunggu di luar.

Ubah Request dalam batch dan tekan Panjang request baru untuk mengambil set lain. Utilisasi sama dengan jumlah semua panjang dibagi n × panjang terpanjang, jadi satu jawaban panjang menurunkannya, berapa pun jawaban pendek yang satu batch dengannya. Panjang output tidak diketahui sampai sequence mengeluarkan end token, jadi static scheduler tidak bisa mengurutkan request berdasarkan panjang sejak awal. Stack serving awal yang menggabungkan request utuh mengalami masalah persis seperti ini.

Langkah 3 dari 6: Iteration-Level Scheduling

Masuk dan keluar batch di setiap langkah

Slot batch4
Kedatangan (14 request dengan seed sama untuk kedua scheduler)Static batching0 selesai, 0% sibuk1234Continuous batching0 selesai, 0% sibuk1234request di-decodeslot ditahan, diamkosong atau mengantre
Langkah decode
0 / 32
Token, static
0
Token, continuous
0
Selesai, static vs cont.
0 vs 0

Kedua scheduler menerima 14 request yang sama pada 4 slot. Tekan Putar atau Langkah untuk menjalankan satu langkah decode setiap kali.

Tekan Putar atau Langkah, lalu ubah Slot batch dan putar ulang. Orca (Yu et al., OSDI 2022) menamai cara ini iteration-level scheduling: scheduler menentukan anggota batch sebelum setiap forward pass, bukan sekali per request. Orca juga memakai selective batching. Linear layer memproses setiap token dalam batch sebagai satu matriks, sedangkan attention berjalan per sequence, karena panjang cache tiap sequence berbeda. Linimasa ini tidak menyertakan prefill, yang dibutuhkan request baru sebelum token pertamanya.

Langkah 4 dari 6: Memori Menentukan Batch

KV cache habis lebih dulu daripada compute

Panjang context per sequence (token)4,096
Ukuran batch (sequence)16
Memori pada satu GPU 80 GB13.5 GB + 16 × 4,096 × 512 KiB = 47.8 GB80 GBweight 13.5 GBKV cache 34.4 GBUkuran batch064128192256batas memori 30compute-bound mulai ~150weight dan cache fp16; aktivasi dan buffer runtime tidak dihitung
KV per sequence
2.1 GB
Batch maks yang muat
30
Batch untuk compute-bound
~150
Memori terpakai
47.8 GB

Pada 4.096 token, 66,5 GB yang tersisa setelah weight menampung 30 sequence, jauh di bawah ~150 yang dibutuhkan agar unit compute tetap sibuk. Di sini memori cache yang membatasi batch.

Geser Panjang context per sequence dan Ukuran batch, lalu lihat batas memorinya bergerak. Batasnya adalah (80 GB − 13,5 GB) ÷ (context × 0,5 MiB): pada 4.096 token, tiap sequence butuh 2,1 GB, jadi 30 yang muat. Penanda ~150 berasal dari ridge point GPU. Decode mengerjakan sekitar 2 FLOP per parameter per token dan membaca 2 byte per parameter dalam fp16, jadi butuh sekitar 150 sequence untuk mencapai 150 FLOP per byte. Penanda ini mengabaikan pembacaan cache, yang mendorong ambang sebenarnya lebih tinggi.

Langkah 5 dari 6: Paged KV Cache

Alokasikan blok berukuran tetap sesuai kebutuhan

Panjang cadangan per request (token)4,096
Ukuran blok (token)16
8 request, panjang akhir dengan seed tetap (prompt + output)batang atas: cadangkan panjang maksbatang bawah: blok 16 tokenR1256R2209R31281R4660R5695R6487R71052R81891token tersimpanteralokasi, kosongtoken (cadangan)
Terbuang, cadangan maks
13.8 GB
Terbuang, blok 16 token
40 MB
Request muat, cadangan
30
Request muat, paged
153

Mencadangkan 4.096 token per request membuat 80% memori cadangan itu kosong. Dengan blok 16 token, hanya blok terakhir tiap request yang punya ruang sisa, total 40 MB, jadi sekitar 153 request dengan komposisi ini muat, bukan 30.

Geser Panjang cadangan per request dan Ukuran blok, lalu bandingkan dua batang di tiap baris. PagedAttention (Kwon et al., 2023) meminjam ide memori virtual dari sistem operasi: block table per request memetakan blok logis ke blok fisik di mana saja dalam memori GPU, jadi area memori yang bersambung tidak diperlukan. Paper vLLM mengukur bahwa sistem sebelumnya hanya memakai 20,4% sampai 38,2% memori KV cache mereka untuk state token yang sebenarnya. Ukuran blok default-nya 16 token, dan pemborosan paling banyak satu blok yang terisi sebagian per request.

Langkah 6 dari 6: Throughput vs Latency

Time to first token, time per token, chunked prefill

Laju kedatangan (request/s)10
Ukuran batch maks32
Chunked prefill
Forward pass terlama di setiap irisan waktu (ms)04080120160rata-rata waktu per token outputwaktu simulasi: 60 request selama 8.5 s, batang dibatasi 160 mspass normalstall (> 3× rata-rata)
Throughput
980 tok/s
Rata-rata time to first token
360 ms
Rata-rata time per output token
22 ms
Pass terlama
291 ms

Setiap prompt baru di-prefill dalam satu pass, dan semua sequence yang sedang di-decode menunggunya: pass terlama memakan 291 ms. Nyalakan chunked prefill untuk menyebarkan pekerjaan itu ke beberapa pass.

Ubah Laju kedatangan dan Ukuran batch maks, lalu nyalakan atau matikan Chunked prefill. Simulasi ini mengirim 60 request dengan seed tetap, dengan prompt 256 sampai 2.048 token dan output 32 sampai 256 token, lewat model GPU ilustratif yang sama. Time to first token mencakup antrean ditambah prefill. Time per output token adalah jeda antar-token berikutnya. Sarathi-Serve (Agrawal et al., 2024) mengusulkan chunked prefill: prompt panjang dipecah menjadi potongan yang berbagi pass dengan decode yang sedang berjalan, sehingga tidak lagi menghentikannya.

Apa itu continuous batching dalam serving LLM?

Continuous batching adalah metode penjadwalan untuk inference LLM yang menambahkan request baru ke batch yang sedang berjalan dan mengeluarkan yang sudah selesai di setiap langkah decode, tanpa menunggu seluruh batch selesai. Server men-decode banyak sequence bersama-sama karena satu pembacaan weight model bisa melayani semuanya. Dengan continuous batching, slot yang dikosongkan jawaban pendek langsung terisi lagi di langkah berikutnya, jadi GPU jarang menganggur sambil menunggu jawaban panjang selesai.

Semua mesin serving LLM utama memakainya, termasuk vLLM, Hugging Face Text Generation Inference, TensorRT-LLM (yang menyebutnya in-flight batching), dan SGLang. Metode ini biasanya dipasangkan dengan paged KV cache, karena jumlah sequence yang bisa dijalankan GPU sekaligus dibatasi oleh memori cache. Kedua ide ini menaikkan throughput serving beberapa kali lipat dibanding batching per request, dan menentukan angka latency yang dirasakan pengguna: time to first token dan time per output token.

Ide utama

  • Decoding dibatasi oleh memori, jadi batching sequence membagi satu pembacaan weight dan menaikkan throughput.
  • Static batching menahan setiap slot sampai request terpanjang dalam batch selesai, sehingga banyak slot menganggur.
  • Continuous batching memasukkan dan mengeluarkan request di setiap langkah decode, sehingga slot tetap penuh.
  • KV cache, bukan compute, biasanya yang membatasi jumlah sequence dalam satu batch.
  • PagedAttention menyimpan cache dalam blok berukuran tetap, sehingga pemborosan paling banyak satu blok parsial per request.
  • Batch yang lebih besar menaikkan throughput tetapi memperlambat tiap token, dan chunked prefill membatasi stall akibat prompt baru.

Ulasan singkat tentang topik ini

Pertanyaan umum

Apa bedanya continuous batching dengan dynamic batching?

Dynamic batching mengumpulkan request yang datang dalam jendela waktu singkat dan menjalankannya sebagai satu batch dari awal sampai akhir. Cara ini cocok untuk pekerjaan berukuran tetap seperti klasifikasi gambar. Continuous batching mengubah batch di setiap langkah token, sehingga cocok untuk generation, yang panjang output-nya sangat beragam dan tidak diketahui sejak awal.

Apakah continuous batching mengubah output model?

Tidak. Setiap sequence hanya memperhatikan token miliknya sendiri, jadi request lain dalam batch tidak memengaruhi logit-nya. Perbedaan numerik kecil bisa muncul karena kernel menjumlahkan dengan urutan berbeda untuk bentuk batch yang berbeda. Dengan greedy decoding, hal ini sesekali bisa membalik dua token yang nilainya nyaris seri.

Apa yang terjadi ketika KV cache penuh?

Scheduler berhenti menerima request baru, dan jika sequence yang sedang berjalan masih butuh blok tambahan, sebagian di-preempt. vLLM memindahkan blok yang di-preempt ke memori CPU, atau membebaskannya lalu menghitung ulang cache nanti dari token yang sudah dihasilkan. Request yang di-preempt dilanjutkan setelah memori kosong.

Kenapa benchmark serving melaporkan throughput sekaligus latency?

Server bisa menaikkan total token per detik dengan memuat lebih banyak sequence ke tiap pass, sementara tiap pengguna melihat token yang lebih lambat dan menunggu lebih lama untuk token pertama. Throughput mengukur biaya per token, dan latency mengukur pengalaman satu pengguna. Batas serving biasanya ditetapkan sebagai target latency, lalu throughput dimaksimalkan di bawah target itu.

Penjelasan terkait

Referensi

  1. Yu et al. (2022), "Orca: A Distributed Serving System for Transformer-Based Generative Models", OSDI '22
  2. Kwon et al. (2023), "Efficient Memory Management for Large Language Model Serving with PagedAttention"
  3. Agrawal et al. (2024), "Taming Throughput-Latency Tradeoff in LLM Inference with Sarathi-Serve"
  4. Pope et al. (2022), "Efficiently Scaling Transformer Inference"

Terakhir diperbarui