Ling-3.0-flash:EPQ MoE 專家分頁實驗
這是 Ling-3.0-flash Q4_K_M 的 MoE expert paging 實驗,採用 EPQ
(MoE Expert Paging + Quantization)命名。模型權重不包含在本 repo,來源為
inclusionAI/Ling-3.0-flash-GGUF
的 Q4_K_M/ 兩個 shard,總計 77,010,144,672 B = 71.72 GiB。
核心機制是:將未啟用的 experts 留在 SSD,將目前路由選中的 experts 讀入有限 RAM
arena,再由 CPU 執行量化 MoE 計算。這不是 Sparse Decomposed Quantization;
sdq-ling/ 是早期移植留下的 implementation 名稱,對外名稱使用 EPQ。
快速開始
git clone https://huggingface.co/HelloSun/ling30flash-moe-expert-paging-q4
cd ling30flash-moe-expert-paging-q4
./sdq-ling/serve.sh
server 預設在 http://0.0.0.0:22222:
curl -s localhost:22222/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{"messages":[{"role":"user","content":"What is the capital of France?"}],"max_tokens":48,"temperature":0}'
也可以指定 pager 預算:
PORT=22222 RAM_BUDGET_MB=32768 ./sdq-ling/serve.sh
模型與路由
下表已對 Q4_K_M-00001-of-00002.gguf 的 GGUF metadata 逐項核對過。
| 項目 | 數值 | 來源 |
|---|---|---|
| 架構 | bailingmoe3 |
GGUF general.architecture |
| 模型檔 | 77,010,144,672 B = 71.72 GiB | 兩 shard 實際檔案大小 |
| MoE experts | 41 層、每層 512 experts | expert_count=512;41 層 MoE(另 2 層 dense + 1 層 MTP) |
| 每 token 啟用 | 8 experts;8 組中先選 4 組 | expert_used_count=8、expert_group_count=8、expert_group_used_count=4 |
| expert 權重 | 約 69.46 GiB,占總權重 96.8 % | 非 expert 權重 2.26 GiB |
| 量化 | gate/up 為 Q4_K;down 為 Q4_K/Q6_K | 逐 tensor 檢查 |
| context metadata | 262,144 | bailingmoe3.context_length |
嚴格 RAM 上限實測
每一輪都在核心層硬上限下執行,不是只把數字傳給 pager:
prlimit --data=<level> --nofile=65536 sdq-chat ... --ram-budget-mb <level>
Linux 上 RLIMIT_DATA 涵蓋 brk() 與所有 mmap 的匿名記憶體,超限會直接 OOM。
swap 則在每輪抽樣 /proc/<pid>/status 的 VmSwap,確認為 0。
測試條件:Intel Xeon Platinum 8559C(192 threads)、ctx=2048、ubatch=128、
threads=8、temp=0、每輪生成 48 token、每級重跑 2 次;每輪前以
posix_fadvise(DONTNEED) 把 71.72 GiB 的 shard 逐檔案逐出 page cache。
| RAM 上限 | 結果 | arena | slots | anon 峰值 | 餘量 | RSS 峰值 | decode tok/s | SSD MiB/tok | hot % | swap |
|---|---|---|---|---|---|---|---|---|---|---|
| 4 GiB | PASS | 1.31 GiB | 397 | 3,065 MB | 1,031 MB | 4,826 MB | 3.38 | 582.2 | 35.9 | 0 |
| 8 GiB | PASS | 4.44 GiB | 1,340 | 6,160 MB | 2,032 MB | 8,250 MB | 7.39 | 64.0 | 66.7 | 0 |
| 16 GiB | PASS | 12.43 GiB | 3,757 | 14,100 MB | 2,284 MB | 16,189 MB | 7.46 | 45.0 | 67.9 | 0 |
| 32 GiB | PASS | 28.44 GiB | 8,592 | 29,985 MB | 2,784 MB | 32,074 MB | 7.45 | 28.8 | 68.9 | 0 |
讀表時請注意三個欄位的差別:
- anon 峰值是唯一受上限管制的量(
smaps_rollup的AnonHWM)。所有 PASS 代表這項確實落在上限之內。 - RSS 峰值較高是預期的:非 expert 權重以 mmap 載入,屬檔案對映頁,
RLIMIT_DATA不管制。這部分可被核心回收,所以不算在 arena 預算裡。 - tok/s 在本機跨設定不可比:同一設定重複量測跨度可達 ±20 %,主機還有其他租戶。 判斷 arena 價值的依據應是 hot % 與 SSD MiB/tok,這兩項是單調的 (582 → 64 → 45 → 29 MiB/tok)。
4 GiB 與 AMX 的取捨
要在 4 GiB 內啟動,必須關掉 AMX。原因是 -march=native 會定義
__AMX_INT8__,llama.cpp 因此要一塊 1,876.34 MiB 的 AMX repack buffer,
在 4 GiB 內光這一塊就吃掉將近一半預算。
cmake -S llama.cpp -B llama.cpp/build_noamx -DCMAKE_BUILD_TYPE=Release \
-DLLAMA_CURL=OFF -DGGML_NATIVE=OFF -DGGML_AVX512=ON \
-DGGML_AVX512_VBMI=ON -DGGML_AVX512_VNNI=ON -DGGML_AVX512_BF16=ON \
-DGGML_AVX2=ON -DGGML_FMA=ON -DGGML_F16C=ON \
-DGGML_AMX_INT8=OFF -DGGML_AMX_TILE=OFF -DGGML_AMX_BF16=OFF
上表的 4 GiB 一列使用這個 build;8 GiB 以上兩種 build 都能通過
(AMX build 的 8 GiB 實測 anon 峰值 6,608 MB,修正後預設 SDQ_RESERVE_MB=3600)。
4 GiB 下 arena 只剩 1.31 GiB(397 槽),hot % 掉到 35.9 %、SSD 讀取 582 MiB/tok —— 能跑,但比 8 GiB 慢一個數量級。若預算有 8 GiB,就不要選 4 GiB。
已知陷阱:SDQ_RESERVE_MB 決定成敗
pager::alloc_arena() 在 llama.cpp 載入權重之前就算 arena,此時
rss_now 只有約 0.09 GiB,因此之後長出來的所有匿名配置都必須由 reserve
手動扣掉。這些包含 AMX 模型緩衝(1,876 MiB)、CPU compute buffer
(83.5 MiB @ubatch=128)、KV/SSM state 與執行緒堆疊。
舊的預設值 900 MB(舊實驗用 1,024 MB)只涵蓋後三項,漏掉 AMX buffer。 實測結果:
SDQ_RESERVE_MB |
RAM 預算 | 實際 anon 峰值 | 結果 |
|---|---|---|---|
| 1,024 MB | 8 GiB | 10.96 GiB | 超出上限,OOM |
| 3,600 MB | 8 GiB | 6.61 GiB | PASS |
| 2,700 MB | 4 GiB | 3.07 GiB | PASS(需關閉 AMX) |
| 2,600 MB | 4 GiB | — | bad_alloc |
已將 sdq_pager.cpp、serve.sh、memory_levels_ling.py 的預設值改為
3,600 MB。若你的環境不需要 AMX,可以再往下調以換更多槽位。
I/O 稽核(已重跑)
mincore(2) 掃過整個模型檔,搭配 buffered 對照組:
| A:預設(O_DIRECT) | B:對照組(buffered + 保留 cache) | |
|---|---|---|
真正從 block device 讀入(read_bytes) |
23.65 GiB | 22.22 GiB |
模型檔留在 page cache 的峰值(mincore) |
2.095 GiB | 22.219 GiB |
重跑結果與原版一致(2.09 / 22.2 GiB)。2.095 GiB 就是非 expert 權重; 對照組證明這個量測有鑑別力。69.46 GiB 的 expert 權重沒有任何一個位元組進過 page cache。
正確性(已重跑)
| 檢查 | 結果 |
|---|---|
| 單 expert 前向 × 7 層 vs 純 NumPy | 7/7 PASS,max_rel ≤ 0.019,corr ≥ 0.99982(重跑數值與原版逐位元相同) |
| 真實圖上整層 MoE(routing + 加權)vs NumPy | PASS,corr 0.99988 |
NumPy dequant vs ggml dequantize_row_* |
21/21 列逐位元組相同 |
更正:原
final_results.md宣稱整層驗證做了 layer 3 與 layer 20, 但當時只留下 layer 20 的輸出。單 expert 測試(verify_selftest.txt)本來 就涵蓋 layer 3,兩邊都有。
限制與注意事項
- RAM budget 與硬性上限不同。
--ram-budget-mb只影響 arena sizing; 要真正設限必須靠 OS(memory.max、RLIMIT_DATA)。上表已用後者驗證。 - 本 repo 的量測環境沒有可用的 GPU(容器內
nvidia-smi無法初始化), 全部為 CPU 執行。 /sys/fs/cgroup在此容器為唯讀,無法再切子 cgroup,因此用prlimit施加上限;RLIMIT_DATA對匿名記憶體的管制語意與memory.max相同, 但不包含 page cache。- 8 GiB 以上的主機也可改用原生全載入路徑(需約 71.72 GiB RAM)。
- Q4_K_M 在 greedy near-tie prompt 可能有量化輸出差異。
- 模型權重授權與使用條件請以 upstream model card 為準。
證據
results/strict_memory_levels.md:本機硬上限 RAM 矩陣(4/8/16/32 GiB,swap=0)results/strict_memory_levels_final.json:上述資料 + 每輪原始紀錄results/host_capability_audit.txt:量測主機/容器硬體能力results/io_audit_rerun.log:O_DIRECT 與 page cache 稽核重跑results/verify_*_rerun.txt:三項正確性檢查重跑輸出sdq-ling/strict_bench.py:本次硬上限實驗腳本(可重跑)results/final_results.md:原始效能摘要(pager budget 口徑)sdq-ling/:legacy implementation source 與 patch
Model tree for HelloSun/ling30flash-moe-expert-paging-q4
Base model
inclusionAI/Ling-3.0-flash-GGUF