Quay lại blog

Khắc phục sự cốHướng dẫn

Xử lý prompt của llama.cpp chậm 35 lần? Sửa KV cache q5_1

Flash attention cộng với KV cache q5_1 có thể đẩy attention sang CPU. Cách xác nhận, sửa bằng q8_0, và khi nào API đơn giản hơn.

Bởi CheapTokens TeamĐã xuất bản 7 phút đọc

Terminal hiển thị GPU ở 0 phần trăm và CPU ở 930 phần trăm trong khi llama.cpp xử lý prompt
Trên trang này
  1. Trông như thế nào trên màn hình
  2. Tại sao cache nhỏ hơn lại làm chậm đến vậy
  3. Kiểm tra trong hai phút
  4. Cách sửa 1: dùng q8_0 (hoặc build lại)
  5. Khi nào tinh chỉnh không còn đáng nữa
  6. Thứ tự cần làm

Mô hình cục bộ của bạn trả lời ổn, nhưng giờ mỗi prompt dài lại treo ở đó cả phút rưỡi trong khi GPU không làm gì. Nếu bạn chạy llama.cpp với -fa on và KV cache q5_1, đó có thể là nguyên nhân: bản build CUDA mặc định không có kernel flash-attention cho loại cache đó, nên bước attention lặng lẽ chạy trên CPU. Chuyển cache sang q8_0 đã đưa một người báo cáo từ 38 lên 1348 token/s khi xử lý prompt.

Các con số dưới đây đến từ issue #28455 của llama.cpp (mở ngày 5 tháng 9 năm 2026, build 10791, kiểm tra ngày 28 tháng 10 năm 2026). Một người báo cáo, một RTX 4080, một mô hình 27B, nên hãy đọc chúng như một ví dụ minh họa, không phải quy luật. Phân tích nguyên nhân gốc cũng là của họ: một maintainer gọi đó là "một vấn đề đã biết" và đóng issue vào ngày hôm sau.

Trông như thế nào trên màn hình#

Thiết lập khá bình thường. Ai đó muốn context 131072 token trên một card 16 GB, nên họ thu nhỏ KV cache xuống q5_1:

bash
llama-server -m model.gguf -ngl 99 -fa on -c 131072 -np 4 \
  --cache-type-k q5_1 --cache-type-v q5_1

Sau đó họ gửi một prompt 3.6K token. Xử lý prompt chạy ở 38.4 token/s và giải mã ở 6.5 token/s. Thử chia: 3,600 ÷ 38.4 = 93.75, tức khoảng 94 giây trước từ đầu tiên. Với q8_0 cùng prompt đó chạy ở 1348 token/s: 3,600 ÷ 1,348 = 2.67, dưới ba giây.

Dấu hiệu nằm ở task manager. Trong các lần chạy chậm, GPU gần như 0% trong khi CPU hiển thị khoảng 930% (chín hoặc mười core bận). Một GPU nhàn rỗi trong khi bạn "đang chạy trên GPU" là triệu chứng cần tìm.

Tốc độ xử lý prompt với KV cache q5_1 và q8_0
Prompt eval trên prompt 3.6K token, RTX 4080, llama.cpp issue #28455 (5 tháng 9 năm 2026)

Tại sao cache nhỏ hơn lại làm chậm đến vậy#

Flash attention là một kernel GPU hợp nhất. Bản build CUDA mặc định chỉ cung cấp nó cho một số cặp loại KV cache. Với q4_0, q4_1, q5_0 và q5_1 không có kernel, nên llama.cpp không dừng lại với lỗi. Nó chuyển thao tác đó sang CPU và tiếp tục. Mọi thứ vẫn hoạt động, chỉ chậm hơn khoảng 35 lần khi xử lý prompt (1348 ÷ 38.4 = 35.1).

Chính chữ "lặng lẽ" đó là toàn bộ vấn đề. Không có gì trong câu trả lời cho bạn biết. Xử lý prompt giảm nhiều nhất (khoảng 35×), và sinh token cũng chậm đi: 6.5 token/s so với 30.1 với q8_0 trong cùng issue.

Kiểm tra trong hai phút#

Chạy llama-bench với đúng các loại cache bạn dùng trong server, mỗi biến thể một lần, và theo dõi GPU ở terminal khác:

bash
llama-bench -m model.gguf -ngl 99 -fa 1 -ctk q8_0 -ctv q8_0 -p 512 -n 0
llama-bench -m model.gguf -ngl 99 -fa 1 -ctk q5_1 -ctv q5_1 -p 512 -n 0
nvidia-smi -l 1

Kết quả pp512 của người báo cáo trên Qwen 27B Q5_K_XL: 1511.92 token/s cho q8_0/q8_0, khoảng 197 cho q5_1/q5_1 trên bản build mặc định. Nếu hai dòng của bạn cho thấy khoảng cách như vậy và nvidia-smi cho thấy GPU nhàn rỗi trong lần thứ hai, bạn đã tìm ra nó.

Trước khi sửa bất cứ thứ gì, hãy đối chiếu những gì bạn thấy với nguyên nhân khả dĩ:

Ghép cặp

Ghép mỗi triệu chứng với nguyên nhân của nó

  • GPU ở ~0%, CPU ở ~930% trong một prompt dàiAttention đang chạy trên CPU
  • Giải mã giảm xuống ~6.5 token/s so với 30.1 với q8_0Sinh token cũng chậm đi, không chỉ xử lý prompt
  • Cùng các flag đó lại nhanh với q8_0Cặp loại cache này không có kernel flash-attention GPU trong bản build mặc định

Cách sửa 1: dùng q8_0 (hoặc build lại)#

Cách sửa nhanh nhất là đổi hai flag và giữ nguyên mọi thứ khác:

bash
llama-server -m model.gguf -ngl 99 -fa on -c 131072 -np 4 \
  --cache-type-k q8_0 --cache-type-v q8_0

Trong issue này, nó cho 1348 token/s khi xử lý prompt và 30.1 token/s khi giải mã (tăng từ 6.5). Cái giá là bộ nhớ: cache q8_0 lớn hơn q5_1, nên hãy kiểm tra xem độ dài context của bạn còn vừa VRAM không.

Nếu bạn buộc phải giữ q5_1 vì lý do bộ nhớ, hãy build lại với tất cả tổ hợp cache flash-attention được biên dịch vào:

bash
cmake -B build -DGGML_CUDA=ON -DGGML_CUDA_FA_ALL_QUANTS=ON
cmake --build build --config Release -j

Bản build đó chạy q5_1 ở 1509.70 token/s trên pp512 và 1162 token/s trên prompt 3.6K token, với giải mã ở 38.5 token/s. Một người bình luận trên issue chỉ ra rằng việc chuyển công việc giữa các backend có thể thay đổi kết quả số học một chút và đề xuất so sánh perplexity giữa các bản build. Nếu bạn quan tâm đến đầu ra chính xác, hãy chạy một phép so sánh.

So sánh

Cách sửa nào phù hợp với card của bạn?

Cache q8_0

Đổi --cache-type-k và --cache-type-v thành q8_0. Không cần build lại. Dùng nhiều VRAM hơn q5_1, nên hãy kiểm tra lại kích thước context bạn có thể chứa.

Build lại với FA_ALL_QUANTS

Giữ q5_1 và build với -DGGML_CUDA_FA_ALL_QUANTS=ON. VRAM như cũ, nhưng bạn phải tự duy trì bản build và mất nhiều thời gian biên dịch hơn.

Câu trả lời "vấn đề đã biết" của maintainer không nói liệu các phiên bản llama.cpp hiện tại đã thay đổi điều này chưa. Cách kiểm tra trung thực là cặp llama-bench ở trên: nếu cả hai dòng gần nhau, bạn ổn.

Khi nào tinh chỉnh không còn đáng nữa#

Giả sử bạn đã làm cho bản sửa hoạt động, và giờ bạn đang cân nhắc vòng tiếp theo: thêm flag, thêm GPU thứ hai, một quant nhỏ hơn. Nếu công việc là tóm tắt, review code hay trích xuất trên các prompt vài nghìn token, hãy hỏi một request tốn của bạn bao nhiêu thời gian và công sức, không chỉ bao nhiêu watt.

Đây là một khối lượng công việc cụ thể để so sánh. Giả sử một job tóm tắt gửi 3,600 token đầu vào và nhận lại 500, 1,000 lần. Trên CheapTokens đó là khoảng $0.104 trên DeepSeek-V4-Flash (context 380K, tối đa 32,768 token đầu ra mỗi câu trả lời, một mức giá bất kể giờ nào) và khoảng $0.092 trên GLM-5.3-Flash (context 390K). Bảng tính cả đầu vào và đầu ra cộng lại, với một cột OpenRouter để so sánh:

Mô hình Mỗi yêu cầu 1.000 yêu cầu OpenRouter, 1.000 yêu cầu
GLM-5.3-Flash $0.000092 $0.092 $0.187
DeepSeek-V4-Flash $0.0001 $0.104 $0.249
MiniMax-M2.7 $0.00026 $0.255 $1.68

3.600 token đầu vào và 500 token đầu ra mỗi yêu cầu. Giá trên 1 triệu token là giá hiện hành và thay đổi cùng với biểu phí của chúng tôi; cột OpenRouter hiển thị giá tham chiếu cho cùng khối lượng công việc.

API không quan tâm đến loại cache, VRAM hay flag build của bạn; không có gì phải chuyển sang CPU. Hãy gửi thử một vài prompt thật của bạn trước và tự đánh giá câu trả lời. Bước này nhỏ: endpoint nói định dạng OpenAI Chat Completions, nên hầu hết client cục bộ chỉ cần một base URL mới.

bash
curl https://api.cheaptokens.dev/v1/chat/completions \
  -H "Authorization: Bearer $CHEAPTOKENS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "deepseek-ai/DeepSeek-V4-Flash-0731", "messages": [{"role": "user", "content": "Summarize the text below in 5 bullets: ..."}], "max_tokens": 500}'

Thiết lập đầy đủ có trong tài liệu CheapTokens, và giá hiện tại từng mô hình nằm trên trang giá. Để xem kỹ hơn về phần cứng so với API, xem Chạy DeepSeek cục bộ hay dùng API. Nếu bạn muốn xem đầu vào và đầu ra được tính phí riêng thế nào, token đầu vào vs đầu ra đề cập đến điều đó.

Thứ tự cần làm#

Sắp xếp thứ tự

Sắp xếp các bước khắc phục theo thứ tự

  1. Chạy llama-bench cho q8_0 và q5_1 và theo dõi nvidia-smi
  2. Đổi cả hai loại cache sang q8_0
  3. Kiểm tra độ dài context vẫn vừa VRAM
  4. Chỉ build lại với GGML_CUDA_FA_ALL_QUANTS=ON nếu bạn cần q5_1
  5. So sánh cùng prompt trên một API nếu thiết lập cục bộ vẫn không đáng công

Một lưu ý nữa về nguồn: toàn bộ chẩn đoán dựa trên một issue công khai và một GPU. Hãy coi 1348 và 38.4 là những gì đã xảy ra ở đó, và đo trên card của chính bạn trước khi tin vào bất kỳ con số nào.