SDQwen38-27B

Qwen3.8-27B 在 3GB 總 RAM、禁止 swap 的條件下推論。權重與 KV cache 全部放在 SSD。

上下文 峰值總 RSS swap 推論
驗證 1,024 0.35 – 0.67 GiB 0 ✅ all_passed: true
理論最高 262,144 2.118 GiB 0 ✅ 實際推理成功

1K 的峰值每次跑會在 0.35–0.67 GiB 之間跳(validate/ 裡有多次紀錄)—— 差異來自 SSD 上多少權重頁剛好還在 page cache。區間的上緣 0.67 GiB 才是要參考的數字, 因為約束要對最壞情況成立。三次量測的 swap 都是 0。

模型:unsloth/Qwen3.8-27B-GGUF / Qwen3.8-27B-UD-Q4_K_M.gguf(15.33 GiB,dense) 引擎:llama.cpp 0504396 + 本專案兩個 patch(見下) 證據:validate/ 每一個數字都有對應檔案


這個專案在解決什麼

27B 模型在 3GB RAM 裡跑,權重本身就佔 15.33 GiB。Dense 模型的特性是 每個 token 都要用到全部權重,而且零重複使用 —— 所以權重不值得留在 RAM 裡 (留著只是多付 15.33 GiB),正確做法是每次從 SSD 重讀。

層 大小 位置
權重 15.33 GiB SSD(mmap + 背景 sweeper 定期丟回)
KV cache @262144 4.78 GiB SSD(檔案 mmap,本專案 patch 0002)
SSM 狀態 / compute buffer / 執行檔 ~0.3 GiB RAM

RAM 裡只有「執行必須」的部分。

為什麼這不容易

三個每一個都真的踩過(詳見 AGENTS.md 的坑表):

  1. mmap 的權重頁會算進 RSS,而且真的佔用系統 RAM。只算匿名記憶體會得到 「0.84 GiB ✓」這種假通過(實際總 RSS 是 15.2 GiB)。
  2. 行程內 madvise 不夠。它能降低行程 RSS,但權重頁仍留在系統 page cache, 會把 16 GB 的 cgroup 灌滿 → 核心反過來把行程自己的匿名頁換出到 swap。 必須搭配外部 posix_fadvise 才算真的沒佔 RAM。
  3. KV 在 262144 下是 4.78 GiB,連最省的 q4_0 都超標 1.6 倍,而且禁止 swap 意味著「讓核心去換 KV」這條路被堵死 → 必須把 KV 本身搬到 SSD。

兩個 patch(都在 patches/,未改動任何上游原始碼)

patch 做什麼
0001-ssd-mmap-release.patch 權重頁 madvise(MADV_DONTNEED),由背景 sweeper 執行緒在計算進行中定期丟回 SSD
0002-kv-ssd-pager.patch KV buffer 改用檔案 mmap,llama_context 的同一個 sweeper 順手把 KV 丟回 SSD

0002 的關鍵設計:不自己寫 buffer type,而是把 CPU buffer 的記憶體來源從 ggml_aligned_malloc 換成檔案 mmap(MAP_SHARED)。因為 set_tensor / cpy_tensor / clear 全部是 memcpy,換掉來源後一行都不用動, 連 q4_0 的寫入路徑都不變 → 零行為差異。

安全性根據:MAP_SHARED 的檔案對映,資料權威副本在檔案, MADV_DONTNEED 只丟 page table entry、不丟資料 —— 對「會被寫入」的 KV 也成立 (writeback 交給核心的 dirty-page 機制,不呼叫 msync)。

為什麼 sweeper 必須在計算進行中跑:單一 token 的 decode 會走完 65 層, 峰值發生在任何「算完才釋放」之前。實測放在 synchronize() 之後時峰值是 8.8 GiB, 改成背景 sweeper 後是 0.35–0.67 GiB。


使用方法

一鍵安裝 + 驗證

git clone https://huggingface.co/HelloSun/sddqwen38-27b
cd sddqwen38-27b

# 編譯 + 下載模型(15.33 GiB)+ 啟動 + 驗證 1K 上下文
export HF_TOKEN=hf_...        # 模型是公開的,但設了會走 xet 加速
./installpi.sh

常用指令

./installpi.sh --validate-only    # 只重跑 1K 驗證(約 8 分鐘)
./installpi.sh --probe-maxctx     # 驗證理論最高 262144(含實際推理,約 10 分鐘)
./installpi.sh --ctx 8192         # 用任意上下文啟動

./sync.sh "改了什麼"               # 推回 HF(崩潰後的唯一保險)

當成 OpenAI 相容 API 用

installpi.sh 會在 127.0.0.1:8080 啟動 llama-server:

curl http://127.0.0.1:8080/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"qwen3.8-27b-ud-q4_k_m",
       "messages":[{"role":"user","content":"用一句話介紹你自己"}],
       "max_tokens":8}'

搭配 pi coding agent(installpi.sh 會自動寫 ~/.pi/agent/models.json, contextWindow 填 262144):

pi-local -p "你好"

換機器之後

tok/s 是「模型 × 機器」的性質,不是模型的性質。 換機必跑:

python3 tools/io_probe.py ~/models/Qwen3.8-27B-UD-Q4_K_M.gguf

本機實測:325 MB/s(單執行緒)、1213 MB/s(4 threads)。 因為 dense 模型每個 token 都要重讀 15.33 GiB,這個數字直接決定速度 → 每 token 約 13–45 秒。程式碼不需要改,只會變快。


效能:誠實說明

數值
prefill 0.46–0.48 tok/s
decode 0.022 tok/s(每 token 約 45 秒)

這不是「還沒調好」,是 dense 模型 + 這台磁碟的物理下限:每個 token 要從 SSD 讀完整份 15.33 GiB,權重之間零重複使用。

對照 HelloSun/sddqwen35a3b 能跑到 6.28 tok/s,那是因為它是 MoE(35B-A3B,每 token 只用約 3B 權重)。 這個模型沒有這個性質,兩者不可比。

⚠️ 舊版文件寫過「0.33 tok/s」,那是假的。那次驗證沒開 page cache 回收, 整份 GGUF 的 15.33 GiB page cache 就留在系統 RAM 裡 —— 等於偷偷用了 15 GB RAM, 正是 3GB 約束要禁止的事。修好之後速度掉 15 倍,但那才是真實數字。


證據

檔案 內容
validate/validation.json 1K 驗證報告,all_passed: true
validate/memwatch-ctx1024.json 1K 峰值總 RSS / swap / 強制手段
validate/chat-ctx1024.json 1K 實際推論輸出與 timings
validate/maxctx-probe.json 262144 結果摘要
validate/memwatch-maxctx.json 262144 峰值總 RSS 2.118 GiB、swap 0
validate/maxctx-infer.json 262144 實際推論輸出與 timings
validate/io-probe.json 本機 SSD 讀取實測
STATUS.md 進度真相來源(含所有技術決定的理由)
AGENTS.md 續作指引 + 已知的坑(接手前必讀)

接手前請先讀

本機很不穩,agent 可能隨時崩潰,所以 AGENTS.md 存在的唯一理由是 「換一個 agent 也能接著做」。其中記了 5 個「看起來對、實際不對」的坑, 全部都是靠實測抓到的 —— 接手前請讀一遍。

約束只有兩條:行程總 RSS(含 page cache)≤ 3GB,以及 swap 使用量 = 0。 驗證必須同時滿足這兩條,缺一不算通過。

參考資料(未修改)

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support