Thử nghiệm Speculative Decoding trên cụm GPU tại nhà: Tại sao nó không hiệu quả?
Tôi đã dành tối thứ Bảy để kiểm tra kỹ thuật n-gram speculative decoding trên GPU phổ thông. Mặc dù được quảng cáo có thể tăng tốc độ suy luận LLM lên 2-3 lần, thực tế cho thấy nó không mang lại lợi ích đáng kể trên phần cứng gia đình do giới hạn băng thông bộ nhớ. Bài viết cũng chỉ ra một cạm bẫy phổ biến trong việc đo lường hiệu năng mà nhiều người đang mắc phải.

Tôi đã dành tối thứ Bảy để kiểm tra kỹ thuật n-gram speculative decoding trên các GPU dành cho người tiêu dùng. Lời khẳng định là: speculative decoding có thể tăng tốc độ suy luận của Mô hình Ngôn ngữ Lớn (LLM) lên gấp 2-3 lần bằng cách dự đoán các token tương lai và xác minh chúng song song.
Tôi muốn xem điều đó có đúng trên phần cứng thực khi chạy các khối lượng công việc đa dạng hay không. Đa phần là không. Tuy nhiên, hành trình này rất đáng giá và tôi đã phát hiện một cạm bẫy trong đo lường hiệu năng (benchmarking) mà tôi nghĩ rất nhiều người đang mắc phải.
Cài đặt
Phòng lab tại nhà của tôi chạy Kubernetes trên một máy có tên Shadowstack. Hai GPU NVIDIA RTX 5060 Ti (16GB VRAM mỗi cái, tổng cộng 32GB). Tôi sử dụng LLMKube, một toán tử K8s mã nguồn mở do tôi tự xây dựng, để quản lý các khối lượng công việc suy luận LLM với llama.cpp.
Để kiểm tra lần này, tôi đã triển khai hai mô hình:
- Gemma 4 26B-A4B: Mô hình Mixture of Experts (MoE) của Google. Tổng cộng 26 tỷ tham số nhưng chỉ khoảng 4 tỷ tham số hoạt động cho mỗi token. Chạy với tốc độ 88 tok/s trên hệ thống của tôi.
- Qwen3-32B: Một mô hình Dense 32 tỷ tham số. Tất cả tham số đều hoạt động cho mỗi token. Chạy với tốc độ 20 tok/s.
Cả hai đều chạy lượng tử hóa Q4_K_M, bật flash attention, ngữ cảnh 8K, và được chia sẻ trên cả hai GPU.
Một lưu ý nhỏ về lý do mô hình MoE nhanh hơn nhiều: Gemma 4 chỉ kích hoạt một phần nhỏ tham số của nó cho mỗi token, do đó có ít dữ liệu trọng số (weight data) cần đọc từ VRAM hơn trong mỗi lần chuyển tiếp (forward pass). Chi phí định tuyến (routing overhead) của MoE sẽ ăn mòn một phần lợi thế đó, nhưng nó vẫn là một lợi thế lớn trên phần cứng bị giới hạn băng thông.
Tôi đã kiểm tra những gì?
llama.cpp có tích hợp sẵn n-gram speculative decoding. Không cần mô hình nháp (draft model), bạn chỉ cần truyền một vài cờ:
--spec-type ngram-mod
--draft-max 64
--draft-min 48
--spec-ngram-size-n 24
--spec-ngram-size-m 48
Cách hoạt động: llama.cpp xây dựng bảng tra cứu n-gram từ ngữ cảnh gần đây (cả lời nhắc đầu vào và kết quả đầu ra đã tạo). Khi nó phát hiện một mẫu đã thấy trước đó, nó sẽ dự đoán (nháp) một số token tiếp theo và xác minh chúng trong một lần chuyển tiếp duy nhất. Nếu các dự đoán đúng, bạn sẽ nhận được nhiều token với chi phí của một token.
Quan trọng: Đây cụ thể là n-gram speculative decoding, không phải các phương pháp dựa trên mô hình nháp như EAGLE-3 hay Medusa. Những phương pháp đó sử dụng một mô hình riêng biệt được huấn luyện để tạo ra các suy đoán. Việc tra cứu n-gram đơn giản hơn và không cần bất kỳ tệp mô hình bổ sung nào.
Với LLMKube, việc chuyển đổi giữa các cấu hình chỉ là cập nhật trường extraArgs trong InferenceService CRD và để toán tử khởi động lại pod:
spec:
modelRef: gemma4-26b-a4b
extraArgs:
- "--spec-type"
- "ngram-mod"
- "--draft-max"
- "64"
Tôi đã kiểm tra hai biến thể: ngram-simple (tra cứu cơ bản) và ngram-mod (biến thể được khuyến nghị cho các mô hình MoE trong tài liệu llama.cpp).
Kết quả đã đánh lừa tôi
Bài kiểm tra đầu tiên của tôi chạy cùng một lời nhắc 10 lần liên tiếp. Các con số trông thật đáng kinh ngạc:
| Lần chạy | tok/s |
|---|---|
| 1 (lạnh) | 88.3 |
| 2 | 105.7 |
| 3 | 112.4 |
| 5 | 186.4 |
| 8 | 336.5 |
| 10 | 419.5 |
Gần như tăng tốc 5 lần vào lần chạy thứ 10. Tôi đã sẵn sàng viết một bài báo hoàn toàn khác.
Sau đó, tôi chạy 8 lời nhắc khác nhau. Tạo mã, thiết kế API, hàm Go, script bash, giải thích kỹ thuật. Sự đa dạng thực sự.
| Lời nhắc | Cơ bản (tok/s) | + ngram-mod (tok/s) |
|---|---|---|
| Triển khai BST | 88.3 | 94.2 |
| Giải thích toán tử K8s | 88.3 | 88.3 |
| Script giám sát GPU | 88.3 | 87.6 |
| Thiết kế REST API | 88.3 | 88.2 |
| Trình phân tích GGUF trong Go | 88.3 | 88.2 |
| Giải thích tính song song | 88.3 | 88.1 |
| Script benchmark | 88.2 | 88.2 |
| Thiết kế Helm chart | 88.1 | 88.2 |
| Trung bình | 88.3 | 88.2 |
Không cải thiện nào cả. Việc "tăng tốc" 419 tok/s là do bộ nhớ đệm n-gram ghi nhớ các mẫu đầu ra lặp lại. Với các lời nhắc đa dạng, không có gì hữu ích để lưu vào bộ nhớ cache.
Câu chuyện tương tự trên mô hình Dense
Qwen3-32B cho thấy cùng một mẫu. 20.4 tok/s cơ bản, 20.6 tok/s với ngram-simple. Trong phạm vi sai số đo lường.
| Mô hình | Loại | Cơ bản | + ngram-simple | + ngram-mod |
|---|---|---|---|---|
| Gemma 4 26B | MoE | 88.3 | 87.2 (-1.2%) | 88.2 (0%) |
| Qwen3-32B | Dense | 20.4 | 20.6 (+1%) | không kiểm tra |
Tại sao nó không giúp ích trên những GPU này
Nút thắt cổ chai (bottleneck) trên RTX 5060 Ti là băng thông bộ nhớ, không phải sức tính toán (compute). Mỗi token yêu cầu đọc trọng số mô hình từ VRAM. Speculative decoding cố gắng gộp nhiều bước xác minh lại với nhau, nhưng khi bạn đã làm bão hòa tuyến bộ nhớ trong quá trình tạo token đơn, thì không có đủ sức tính toán nhàn rỗi để việc xác minh suy đoán bù đắp được chi phí.
Điều này khác với các GPU dữ liệu trung tâm cao cấp (A100, H100), nơi tỷ lệ sức tính toán trên băng thông bộ nhớ cao hơn nhiều. Một H100 có khoảng 3.350 GB/s băng thông bộ nhớ nhưng gần 2.000 TFLOPS sức tính toán FP16. Tỷ lệ đó có nghĩa là có sức tính toán nhàn rỗi thực sự ở kích thước lô (batch size) nhỏ mà speculative decoding có thể khai thác. GPU dành cho người tiêu dùng không có dư địa đó.
Đặc biệt đối với các mô hình MoE, có một thêm một chút phức tạp. Mỗi token suy đoán trong một lô xác minh có thể kích hoạt các chuyên gia (experts) khác nhau, điều này có nghĩa là nhiều khối trọng số chuyên gia cần được đọc hơn. Điều này làm giảm lợi thế gộp lô mà speculative decoding dựa vào trong các mô hình Dense, nơi việc đọc trọng số vẫn giữ nguyên bất kể kích thước lô.
Lưu ý: Có những trường hợp n-gram spec decoding có thể giúp ích ngay cả trên phần cứng phổ thông. Nếu mô hình của bạn được chuyển một phần sang CPU (không vừa VRAM), nút thắt cổ chai băng thông PCIe đủ nghiêm trọng để việc gộp suy đoán có thể mang lại lợi ích thực sự. Và đối với các đầu ra có tính lặp lại cao hoặc theo khuôn mẫu (như JSON có cấu trúc, mã chuẩn), tỷ lệ hit của bộ nhớ đệm n-gram sẽ tăng lên rất nhiều. Việc kiểm tra của tôi tập trung vào suy luận cho người dùng đơn với các mô hình nằm hoàn toàn trong VRAM và các lời nhắc đa dạng.
Thế còn EAGLE-3 thì sao?
Tôi ban đầu định kiểm tra EAGLE-3, sử dụng một đầu nháp được huấn luyện thay vì tra cứu n-gram. Ba vấn đề:
- Không có mô hình nháp EAGLE-3 nào tồn tại cho Gemma 4 (chưa ai huấn luyện mô hình đó).
- PR EAGLE-3 của llama.cpp (#18039) vẫn đang mở và ở dạng dự thảo tính đến ngày 5 tháng 4 năm 2026.
- Benchmark của chính PR đó cho thấy các mô hình MoE chỉ đạt khoảng 0,89-1,06x trên một số lời nhắc, với một số thậm chí chậm hơn do chi phí kích hoạt chuyên gia trong quá trình xác minh lô.
Ngay cả với đầu nháp được huấn luyện, giới hạn băng thông cơ bản trên GPU phổ thông vẫn sẽ tồn tại.
Điều gì thực sự giúp ích trên GPU phổ thông
Nếu bạn đang chạy LLM cục bộ trên phần cứng phổ thông, đây là những gì thực sự tạo ra sự khác biệt:
- Flash attention: Đã trở thành tiêu chuẩn, tiết kiệm bộ nhớ đáng kể.
- Lượng tử hóa KV cache: q4_0 hoặc q8_0 giảm áp lực bộ nhớ cache mà không làm giảm chất lượng đáng kể.
- MoE thay vì Dense: Gemma 4 kích hoạt ~4 tỷ tham số mỗi token so với 32 tỷ của Qwen3-32B. Đó là động lực chính của sự khác biệt về thông lượng, mặc dù chi phí định tuyến MoE có nghĩa là việc tăng tốc không phải là tỷ lệ sạch 8x.
- Chia đa GPU: Nhân đôi băng thông bộ nhớ khả dụng của bạn, đây là nút thắt cổ chai thực sự.
- Tuning kích thước ngữ cảnh: Ngữ cảnh nhỏ hơn = KV cache ít hơn = nhiều dư địa VRAM hơn.
Bài học về đo lường hiệu năng
Bài học lớn nhất không phải về speculative decoding. Nó là về phương pháp đo lường hiệu năng.
Nếu tôi chỉ kiểm tra với các lời nhắc lặp lại, tôi đã báo cáo mức tăng tốc 4,75x và hoàn toàn sai. Bộ nhớ đệm n-gram đang làm một việc gì đó thực sự, nhưng chỉ trong một kịch bản hẹp nơi đầu ra có tính lặp lại cao hoặc theo khuôn mẫu. Đối với trò chuyện tương tác, hỗ trợ viết mã, hoặc bất kỳ khối lượng công việc nào có đầu vào đa dạng, nó không cung cấp lợi ích nào trên phần cứng này.
Hãy hoài nghi về các benchmark speculative decoding không công bố sự đa dạng của lời nhắc. Và nếu bạn thấy ai đó báo cáo lợi ích n-gram khổng lồ, hãy kiểm tra xem họ có đang chạy cùng một lời nhắc lặp đi lặp lại hay không.
Tự thử nghiệm
Mọi thứ tôi kiểm tra đều chạy trên Kubernetes thông qua LLMKube. Trường extraArgs của InferenceService CRD giúp việc chuyển đổi giữa các cấu hình trở nên dễ dàng mà không cần chạm vào việc triển khai của bạn:
apiVersion: inference.llmkube.dev/v1alpha1
kind: InferenceService
metadata:
name: gemma4-spec-bench
spec:
modelRef: gemma4-26b-a4b
image: ghcr.io/ggml-org/llama.cpp:server-cuda
contextSize: 8192
flashAttention: true
extraArgs:
- "--spec-type"
- "ngram-mod"
- "--draft-max"
- "64"
resources:
gpu: 2
LLMKube là mã nguồn mở, giấy phép Apache 2.0: github.com/defilantech/llmkube
Bài viết liên quan

Công nghệ
Meta đổ lỗi cho thiếu hụt RAM khi tăng giá 100 USD cho tai nghe Quest 3
16 tháng 4, 2026

Công nghệ
Máy tính góc cơ điện tử bên trong hệ thống theo dõi sao của máy bay ném bom B-52
18 tháng 4, 2026
Công nghệ
Fuzix OS 0.4 chính thức phát hành: Cải tiến mạng, định dạng tệp và hỗ trợ phần cứng phong phú
18 tháng 4, 2026
