EPQ:Qwen3.6-35B-A3B 專家分頁與量化 CPU 推理
EPQ(Expert Paging Quantization)是以 llama.cpp 為基礎的 CPU 推論路徑,讓 22.1 GB 的
Qwen3.6-35B-A3B-UD-Q4_K_M.gguf 在路由專家權重無法全部放入 RAM 時仍可執行。
GGUF 權重保留在儲存裝置,只有當前選中的專家會分頁載入容量受限的 RAM arena,
MoE 專家計算則由 CPU 執行。本 repo 收錄程式碼與量測結果,不包含模型權重。
中文描述名:Qwen3-35B-A3B 專家分頁 4-bit 量化 CPU 推理版。
EPQ 的名稱刻意描述實際機制:MoE expert paging、一般 GGUF 權重量化,以及 CPU
低記憶體推理。本專案不是論文中的 SDQ(Sparse Decomposed Quantization):
沒有 2:4 結構化稀疏硬體跳零計算,也不宣稱使用 GPU 稀疏加速。sdq/ Python
package、sdq_* C++ 檔案與 SDQ_* 除錯環境變數是歷史 implementation identifiers,
仍保留以避免破壞既有驗證工具;對外產品名稱與執行入口已改為 EPQ。
快速開始
需求:Linux x86-64、C++ 工具鏈、CMake、Git、Python 3、足以存放 22.1 GB GGUF
與建置檔的磁碟空間,以及可提供實用解碼速度的本機高速 SSD/NVMe。已測試路徑使用
llama.cpp commit 836d57176dc699a726c55418e4f96b8ca628e1bf。
python3 demo.py --once
在其他位置執行時,此腳本會取得本 repo 原始碼,從
unsloth/Qwen3.6-35B-A3B-GGUF 下載 GGUF,建置指定版本的 llama.cpp,然後執行
短篇 CPU 推論。若模型已下載,可使用:
python3 demo.py --model-path /path/to/Qwen3.6-35B-A3B-UD-Q4_K_M.gguf \
--once --ctx 4096 --threads 8 --n-predict 48
互動式聊天:
python3 demo.py --model-path /path/to/Qwen3.6-35B-A3B-UD-Q4_K_M.gguf \
--ctx 4096 --threads 8
此 GGUF 不含 vision projector;本 EPQ 路徑僅支援文字推論。已量測設定為 4K
context,在此 RAM 預算下尚未驗證模型宣稱的更長 context。
記憶體配置方式
模型 metadata 顯示每個 MoE 層有 256 個路由專家,每個 token 選用 8 個專家。GGUF
約 22.1 GB(十進位),其中路由專家權重約 18.2 GiB。EPQ 將未啟用的專家留在檔案
中,按需求把選中的專家載入記憶體 slab cache。此實作使用量化 GGUF 專家權重與
ggml CPU 向量點積 kernel、需求觸發的 LRU 式替換、平行讀取各專家不連續的
gate/up/down 權重區段,以及可與 CPU 計算重疊的專家計算 pipeline。
量測所用建置設定為 ubatch=128、4K context、greedy sampling;測試使用 8 條 CPU
threads。依專家權重大小配置 slab,避免少數較大的專家浪費 arena 空間。跨層預取
預設關閉:在已測 workload 中,它增加流量並使速度退步。此 GGUF 沒有 MTP head;
在已測散文提示中,prompt-lookup n-gram drafting 的接受數為 0。
與傳統 llama.cpp 的同一 GGUF 比較
EPQ 並不是另一個模型。以下比較使用完全相同的
Qwen3.6-35B-A3B-UD-Q4_K_M.gguf,對照未修改的 llama.cpp CPU 路徑。
在同一台主機、8 threads、4K context、3 輪 decode throughput 中位數測試中:
| 執行路徑 |
Decode 中位數 |
相對結果 |
傳統 llama.cpp llama-completion |
12.27 tok/s |
1.00x |
EPQ epq-chat |
6.32 tok/s |
0.515x |
因此,在這組測試條件下,EPQ 比傳統 llama.cpp 慢約 48.5%;換一種說法,
傳統 llama.cpp 約為 EPQ 的 1.94 倍 decode 速度。
這個速度差是以 EPQ 將專家權重限制在約 8 GiB pager budget 的部署目標換來的:
傳統路徑讓整個 MoE 權重透過 mmap/CPU 路徑使用,而 EPQ 將選中的專家從 GGUF
按需讀入約 3.68 GiB arena。因此不能把結果解讀成 EPQ 的純計算 kernel 比
llama.cpp 慢 48.5%。
測試條件
- 測試日期:2026-10-09
- 主機:2× Intel Xeon Platinum 8559C,192 logical CPUs,AVX-512/AMX
- 建置:Linux x86-64,Release,
GGML_NATIVE=ON
- llama.cpp commit:
836d57176dc699a726c55418e4f96b8ca628e1bf
- 模型:
Qwen3.6-35B-A3B-UD-Q4_K_M.gguf,22,134,528,992 bytes
- CPU threads:8;batch:4096;ubatch:128;context:4096
- 生成:48 tokens,temperature 0,seed 42,純 completion,無 GPU
- prompt:
Write a short paragraph about how mixture-of-experts models route tokens.
- 每個路徑執行 3 輪,表格採 decode throughput 中位數
- 每輪獨立啟動程序;模型檔相同,未把 startup/model-load time 算入 decode tok/s
傳統 llama.cpp 指令(未套 EPQ patch):
llama-completion \
-m Qwen3.6-35B-A3B-UD-Q4_K_M.gguf \
-p "Write a short paragraph about how mixture-of-experts models route tokens." \
-n 48 -c 4096 -t 8 -tb 8 -b 4096 -ub 128 \
--temp 0 --seed 42 --no-warmup --no-display-prompt --no-jinja -no-cnv
EPQ 指令:
epq-chat \
-m Qwen3.6-35B-A3B-UD-Q4_K_M.gguf \
-p "Write a short paragraph about how mixture-of-experts models route tokens." \
-n 48 -c 4096 -t 8 -b 128 \
--temp 0 --no-chat --no-interactive --ram-budget-mb 8192
原始輪次
| 路徑 |
Run 1 |
Run 2 |
Run 3 |
中位數 |
| 傳統 llama.cpp |
12.27 |
9.98 |
12.60 |
12.27 tok/s |
| EPQ |
6.45 |
6.32 |
5.78 |
6.32 tok/s |
llama.cpp 的統計以 47 個 decode runs 計算,因為第一個生成 token 不在該計時段;
EPQ 同樣報告 47 decode tokens。EPQ CLI 顯示 15 prompt tokens,傳統 llama.cpp
tokenizer 在相同字串下顯示 14 tokens;這只影響 prefill,不影響上述 decode
throughput 比較。
EPQ 額外指標
| 指標 |
結果 |
| Expert-cache hit rate |
63.3% |
| Expert SSD read |
約 7,813 MB / run,約 166.2 MB/decode token |
| Expert arena |
約 3.68 GiB |
| Peak/ending RSS |
約 7,618 MiB |
| Process VmSwap |
0 MB(該行程觀測值) |
VmSwap=0 MB 不代表主機停用 swap;本次主機有系統 swap,也不是嚴格的 8 GiB
cgroup/no-swap 驗證。EPQ 的 --ram-budget-mb 8192 是 pager sizing budget,
不是整個程序的 RSS 硬上限。要驗證部署保證,仍需使用 memory.max、
memory.swap.max=0 的獨立環境。
完整的中文速度比較報告見
docs/llama_cpp_speed_comparison_zh.md。
不同 RAM 預算比較:4 / 8 / 16 / 32 / 60 GiB
在相同 prompt、8 threads、ctx 4096、ubatch 128、48-token greedy decode、每個
設定 3 輪的測試中,EPQ 的最佳結果是 16 GiB pager budget:6.70 tok/s。
RAM 越大不一定持續變快:16 GiB 後工作集已大部分常駐,32/60 GiB 的差異主要受
CPU、SSD 與單輪執行波動影響。
| RAM pager budget |
實際 arena |
Decode 中位數 |
相對 8 GiB |
Expert cache hit |
SSD 讀取 / token |
結束 RSS |
| 4 GiB |
0.50 GiB |
3.38 tok/s |
0.62x |
0.0% |
599.4 MB |
4,370 MiB |
| 8 GiB |
3.68 GiB |
5.47 tok/s |
1.00x |
63.3% |
166.2 MB |
7,616 MiB |
| 16 GiB |
11.68 GiB |
6.70 tok/s |
1.23x |
74.9% |
86.1 MB |
15,819 MiB |
| 32 GiB |
27.68 GiB |
6.27 tok/s |
1.15x |
74.2% |
91.3 MB |
32,192 MiB |
| 60 GiB |
55.68 GiB |
5.93 tok/s |
1.08x |
74.9% |
86.1 MB |
60,877 MiB |
原始三輪 decode 結果:
| RAM pager budget |
Run 1 |
Run 2 |
Run 3 |
中位數 |
| 4 GiB |
3.39 |
3.20 |
3.38 |
3.38 tok/s |
| 8 GiB |
6.15 |
5.12 |
5.47 |
5.47 tok/s |
| 16 GiB |
6.99 |
6.70 |
5.38 |
6.70 tok/s |
| 32 GiB |
6.35 |
6.27 |
5.65 |
6.27 tok/s |
| 60 GiB |
5.93 |
5.22 |
6.32 |
5.93 tok/s |
以同機傳統 llama.cpp 的 12.27 tok/s 為基準,4/8/16/32/60 GiB 分別約為
基準的 27.5%、44.6%、54.6%、51.1%、48.4%。這些是單一主機、單一 prompt、
3 輪的工程測試,不是跨硬體保證。
注意:--ram-budget-mb 是 EPQ pager sizing budget,不是 OS/cgroup 的總 RSS
硬限制。60 GiB 設定本次實測結束 RSS 約 60.9 GB,請確保主機有足夠實體記憶體,
並且不要把這些測試解讀為 no-swap 驗證。完整測試條件與逐輪結果見上述中文報告。
歷史速度與記憶體結果
以下是 repo 中既有的歷史量測,不是上面同一 GGUF 對照測試的重複結果:
| 量測項目 |
結果 |
條件 |
| Decode throughput |
中位數 6.28 tokens/s |
7 runs、8 threads、ctx 4096、ubatch 128、48 generated tokens |
| Prefill throughput |
約 30 tokens/s |
681-token prompt、ubatch 128 |
| Expert-cache hit rate |
70.7% |
representative decode workload |
| Expert I/O |
約 202 MB/token |
十進位 MB,視 route/cache state 而異 |
| Peak process RSS |
8,035 MiB |
長 prompt/4K-context 壓力測試 |
| Process VmSwap |
0 MB |
該幾輪量測的觀察值 |
以上數值來自 docs/03_results.md。不同階段的 benchmark 主機與方法不同:較早的
速度測試使用 8 GiB RLIMIT_DATA 上限,後續測試則是在較寬鬆的外層限制下將 RAM
預算交給 pager。RLIMIT_DATA 限制 data/heap 配置,不是總常駐記憶體;它不會
停用系統 swap,也不能證明總 RSS 低於 8 GiB。請勿將上述數值解讀為嚴格 8 GiB
實體 RAM/no-swap 驗證。SSD 速度、記憶體壓力、CPU quota、檔案系統快取與 prompt
都會影響 tok/s。
設計依據與實測取捨
此設計利用 MoE 的稀疏專家啟用特性:路由專家會動態選取,因此計算 token 時不需要
把全部權重常駐 RAM。DeepSeekMoE 討論稀疏專家啟用與 shared experts
(Dai 等人,2024);PagedAttention 將分頁用於
serving 系統的 KV cache 管理(Kwon 等人,SOSP 2023)。
這些研究提供記憶體階層設計的背景,但都沒有評估此 CPU/GGUF 實作。
| 技術 |
本地證據/決策 |
| 將專家快取限制在 RAM;未啟用專家留在檔案 |
核心設計;另一組 2-thread 測試中,8 GiB pager 預算結果為 3.45 tok/s。 |
| 平行讀取 gate/up/down |
既有 5 輪比較:5.52 vs 3.73 tok/s,約提升 48%;結果依主機與儲存裝置而異。 |
| 依權重大小配置槽位 |
既有 7 輪比較:6.28 vs 5.63 tok/s,約提升 11.5%,RSS 相近。 |
| Speculative decoding |
此 GGUF 沒有 MTP 權重;本地 n-gram replay 在已測散文提示中的接受數為 0,因此未啟用。 |
| 跨層專家預取 |
本地結果速度退步 8% 且 I/O 增加,故預設關閉。 |
| 對已量化熱專家做無損壓縮 |
本地 Zstd/LZ4 測試僅回收約 0–2.5% 空間,未用於執行期快取。 |
| 更積極的有損專家替代/剪枝 |
可能以品質換吞吐量;此處尚未驗證輸出品質,因此未啟用。 |
最佳化目標是在不更動路由或權重的前提下縮短每個 token 的關鍵路徑。若實驗改變
取樣輸出,必須另行評估品質,才能視為速度提升。
Benchmark 與驗證指令
# 基準測試(假設模型已下載)
python3 -m sdq.bench --model /path/to/model.gguf \
--bin /path/to/llama.cpp/build/bin/epq-chat --ram-mb 8192 --rounds 2
# 比較儲存裝置並行度與槽位大小策略
python3 -m sdq.experiments --model /path/to/model.gguf \
--bin /path/to/llama.cpp/build/bin/epq-chat --suite io --repeat 3
python3 -m sdq.experiments --model /path/to/model.gguf \
--bin /path/to/llama.cpp/build/bin/epq-chat --suite slot --repeat 7
# 檢查目標儲存裝置;direct I/O 支援與隨機讀取表現很重要
python3 tools/io_probe.py --model /path/to/model.gguf
# kernel/layer 檢查與 logits 確定性檢查
python3 demo.py --model-path /path/to/model.gguf --verify
python3 tools/determinism_check.py --model /path/to/model.gguf \
--bin /path/to/llama.cpp/build/bin/epq-chat --runs 5 -n 48
sdq.bench 是 legacy Python package 名稱,預設會套用 RLIMIT_DATA;這不是硬性 RSS 或 no-swap 限制。不要將它
解讀為 OS 記憶體上限。請同時回報 VmRSS/peak RSS 與 VmSwap,若要驗證嚴格部署
限制,請使用停用 swap 的 OS-level cgroup。
Repository layout
cpp/、patches/:C++ expert pager 與 llama.cpp 整合。
sdq/:設定、benchmark、實驗、驗證與 HF sync。
tools/:GGUF 檢查、正確性檢查、route/storage probe。
docs/:設計、完整量測結果、負面結果與環境稽核。
stage.txt:依時間排列的專案狀態。
License
Qwen base model 由 upstream 以 Apache-2.0 發布;請以 upstream model card 的目前
條款為準。本 repository 包含推論軟體與量測結果,不包含模型權重。llama.cpp 與其
相依套件保留各自授權條款。