Đặc thù của LLM inference: mỗi request sinh số token khác nhau nên kết thúc ở thời điểm khác nhau. Cách gom batch quyết định throughput.
Static batching: gom N request, chạy song song, đợi tất cả xong mới trả về. Với batch 4 request có độ dài output [50, 100, 200, 500] token, request đầu xong sau 50 bước nhưng vẫn phải chờ thêm 450 bước — GPU giữ chỗ cho nó suốt thời gian đó mà không sinh ra gì.
Continuous batching đổi đơn vị lập lịch: xếp lịch theo từng bước sinh token, không theo request. Sau mỗi bước, scheduler loại request đã xong khỏi batch và giải phóng bộ nhớ, rồi nạp ngay request đang chờ vào chỗ trống. GPU luôn chạy với batch đầy. Ý tưởng công bố trong Orca (OSDI 2022), nay là mặc định của vLLM, TGI, TensorRT-LLM, SGLang.
Đó là lý do throughput tăng vọt: GPU utilization đi từ khoảng 20–40% lên 80–95%. Phần lớn mức tăng đến từ việc xoá thời gian chờ, không phải từ tính toán nhanh hơn.
Nhưng continuous batching chỉ chạy được nếu quản lý được bộ nhớ. KV cache của mỗi request phình dần theo từng token sinh ra; cấp phát sẵn theo độ dài tối đa thì lãng phí phần lớn VRAM và không còn chỗ nạp request mới — mất hết cái lợi vừa nói.
PagedAttention (vLLM) giải quyết đúng chỗ này, mượn ý tưởng bộ nhớ ảo của hệ điều hành: chia KV cache thành block cố định (ví dụ 16 token), mỗi request giữ một bảng ánh xạ block logic sang block vật lý, chỉ cấp phát khi thực sự cần. Một cái lợi kèm theo: hai request trùng phần đầu prompt thì dùng chung block vật lý — đây chính là cơ chế prefix caching.
Một tranh chấp nữa: prefill và decode. Prefill (nạp prompt) nặng về tính toán, decode (sinh token) nặng về băng thông; trộn chung một batch làm độ trễ giật cục. Chunked prefill cắt prefill thành mảnh nhỏ xen kẽ giữa các bước decode để độ trễ đều hơn.
llm = LLM(
model="meta-llama/Llama-3.1-8B-Instruct",
gpu_memory_utilization=0.9,
max_num_seqs=256, # số request đồng thời tối đa
enable_prefix_caching=True,
enable_chunked_prefill=True,
)Khi không nên dùng: hệ đòi độ trễ ổn định tuyệt đối (giao dịch, thoại realtime) — batch động gây jitter, thà chạy từng request với tensor parallel.
Hoặc tải quá thấp thì cách gom batch nào cũng như nhau.