Card của mọi model local giờ đều khoe cửa sổ 128k. Trên desktop, con số đó là hợp đồng thuê mà RAM của bạn ký mà không đọc. Thứ quyết định bạn đẩy được tài liệu dài cỡ nào không phải trọng số — mà là KV cache, và nó phình theo ngữ cảnh như một loại thuế tính bằng đồng tiền của người khác.
Cửa sổ ngữ cảnh không phải tính năng bật tắt. Nó là hợp đồng thuê bộ nhớ, trả từng giây trong lúc server chạy.
128k ngữ cảnh thực sự tốn bao nhiêu RAM?
Phép tính công khai và ngắn. Như Transformer Inference Arithmetic giải thích, KV cache giữ hai tensor — key và value — cho mỗi layer, mỗi KV head, mỗi token:
cache_bytes = 2 × layer × ngữ cảnh × kv_head × head_dim × byte_mỗi_phần_tử Lấy Llama 3.1 8B: 32 layer, 8 KV head, head_dim 128 — thẳng từ config.json trên Hugging Face. Với 131.072 token ở fp16 (2 byte):
2 × 32 × 131072 × 8 × 128 × 2 = 17.179.869.184 byte ≈ 16 GiB Trọng số của chính model đó ở fp16 nặng ~16 GiB. Ở ngữ cảnh tối đa, cache to bằng chính model nó phục vụ. “Model 8B vừa 16 GiB” lặng lẽ hoá bài toán 32 GiB ngay khoảnh khắc bạn nâng num_ctx lên 128k.
Vì sao cache lớn theo cửa sổ?
Vì attention phải so từng token với mọi token đứng trước, mà suy luận là tự hồi quy — không tính lại gì, nhớ tất cả. Cả mẹo khiến sinh chữ nhanh nằm ở đó: key và value của từng token chỉ tính một lần rồi cất. Bộ nhớ mỗi token là hằng số nên tổng thể tăng tuyến tính theo ngữ cảnh. 128k không phải “con số lớn hơn” — nó là 128 lần cache của cửa sổ 1k, giữ nguyên suốt phiên làm việc.
Grouped-query attention (GQA) là phần giảm giá phía model: ít KV head, cache gọn hơn. Qwen2.5 7B dùng 28 layer nhưng chỉ 4 KV head (config ở đây), nên cùng cửa sổ 128k chỉ tốn ~7 GiB ở fp16 — giảm tải thật sự, và một lý do khiến các model này dễ chịu hơn trên máy nghèo nàn.
| Model (fp16) | Layer × KV head | KV cache @ 128k | Trọng số | Cache so với trọng số |
|---|---|---|---|---|
| Llama 3.1 8B | 32 × 8 | ~16 GiB | ~16 GiB | ~100% |
| Qwen2.5 7B | 28 × 4 | ~7 GiB | ~15 GiB | ~47% |
Tự tính lại — toàn bộ kiểm chứng nằm ở đây:
def kv_cache_gib(layers, kv_heads, head_dim, ctx, bytes_per=2):
return 2 * layers * ctx * kv_heads * head_dim * bytes_per / 2**30
print(kv_cache_gib(32, 8, 128, 131_072)) # Llama 3.1 8B -> 16.0
print(kv_cache_gib(28, 4, 128, 131_072)) # Qwen2.5 7B -> 7.0 Con số marketing bỏ sót gì
Đây là sổ sách trung thực của lập luận này — kể cả chỗ nó cong đi:
- Prefill là hoá đơn thứ hai. Trước token trả lời đầu tiên, cả prompt được xử lý một lượt. Prompt 100k token nghĩa là nhai 100k token trước khi thấy ký tự đầu. Trên 3060, đó là phút chứ không phải mili giây.
- Model sliding-window phá phép tính — theo hướng có lợi cho bạn. Kiến trúc kiểu Mistral giới hạn attention trong cửa sổ cố định mỗi layer nên cache ngừng phình. Công thức đơn giản sẽ đếm thừa cho chúng. Bài này nói về transformer full-attention — đa số thứ người ta đang chạy.
- Lượng tử hoá KV là thật nhưng chỉ một phần. llama.cpp có thể giữ KV ở q8_0 — cache giảm cỡ một nửa, đổi chút rủi ro chất lượng. Nửa của 16 GiB vẫn là 8 GiB.
- Spec ít khi ghi số KV head. Bạn phải đào config.json ra tìm — điều đó tự nói lên con số đó muốn được đọc theo kiểu nào.
Không điều nào ở trên khiến ngữ cảnh dài thành vô dụng. RAG tồn tại chính vì nhét đầy cửa sổ là cách đắt nhất để nói “đọc mục 4”. Nhưng card in “128k” mà không in hoá đơn bộ nhớ là bán xe theo tốc độ tối đa mà không bao giờ nhắc đến bình xăng.
Không ai đọc 128.000 token. RAM của bạn vẫn trả tiền cho đủ cả.
Giải pháp thì nhạt nhẽo: đặt ngữ cảnh đúng khối lượng công việc thực sự dùng. Cửa sổ 32k tốn một phần tư cache 128k và phủ gần hết những prompt một dev đơn lẻ thật sự gửi. Đây chính là kỷ luật sổ sách của phép tính RAM cho LLM local — cỡ model chỉ là nửa ngân sách, còn các mức lượng tử hoá thon trọng số nhưng không đụng tới phép tính cache.
Nếu cache là giá thật của ngữ cảnh, vì sao card bán cửa sổ mà không bao giờ bán hoá đơn? Và phần thú tội: ngữ cảnh lớn nhất bạn thực sự lấp đến token cuối là bao nhiêu — output có đáng mấy GB đó không? Bình luận đang mở; bài tiếp theo của loạt bài sẽ đứng về phía bên thua.
FAQ
Cửa sổ ngữ cảnh 128k tốn bao nhiêu RAM?
Với Llama 3.1 8B ở fp16, một mình KV cache đã nặng ~16 GiB ở 131.072 token — ngang với trọng số của model. Công thức: 2 × layer × ngữ cảnh × KV head × head_dim × byte.
Vì sao ngữ cảnh dài ngốn bộ nhớ hơn?
Mỗi token trong cache giữ tensor key và value cho từng layer attention. Ngữ cảnh gấp đôi, cache gấp đôi. Dù không bao giờ lấp đầy cửa sổ, việc cấp phát vẫn tồn tại.
Giảm bộ nhớ KV cache ở LLM local bằng cách nào?
Đặt ngữ cảnh đúng mức thực dùng, chọn model có grouped-query attention (ít KV head), lượng tử hoá KV cache xuống q8 nếu runtime hỗ trợ, hoặc dùng kiến trúc sliding-window và lai.
— mrsaynothing
— mrsaynothing
Ý kiến đã load-test trước khi xuất xưởng. Phần lớn là vậy.
Nhận bài tranh luận tiếp theo qua email
Một email mỗi bài viết. Đồng ý cũng được, mổ xẻ cũng được.
cái này là gì?git stash: chỉ một file, không đụng đến phần còn lại
Thích kiểu viết này? Tôi build như vậy để kiếm sống. thuê tôi