這個 repo 是推論專案(patch + 量測工具 + 證據),模型權重本身在 unsloth/Qwen3.5-4B-GGUF,不重複發佈。

SDQwen35-4B

Qwen3.5-4B(Q4_K_M, 2.553 GiB)在 1 GiB 總 RAM、禁止 swap 的條件下推論。 權重與 KV cache 放在 SSD 檔案,RAM 只留執行必須的部分。

上下文 峰值總 RSS swap 推論
已驗證 1,024 0.384 GiB 0 ✅ all_passed: true
理論最高 262,144 (尚未實測) — ⚠️ 未驗證

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

⚠️ 誠實說明:262144 上下文還沒實測過。模型本身支援(context_length = 262144, 架構上 KV 有 patch 0002 放 SSD),但「跑得起來」跟「在 1 GiB 預算內跑得起來」是兩件事, 本 repo 目前只對 1K 上下文負責。跑 ./installpi.sh --probe-maxctx 才會產生那個證據。


這個專案在解決什麼

4B 模型只有 2.553 GiB,看起來「1 GiB RAM 跑 4B」很輕鬆。實際上不難,但做法決定差 20 倍以上:

Qwen3.5 是 hybrid 架構:32 層中只有 8 層是 full attention(每 interval 4 層), 其餘 24 層是 linear/SSM。這代表 KV cache 只跟 8 層有關,不是 32 層 —— 這是它比同級模型省 KV 的原因。

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

★ 最重要的結論:權重留 page cache,比全丟 SSD 快 22 倍

這是本專案實測出來、與直覺相反的一件事。兩個配置都符合 1 GiB 約束:

配置 峰值總 RSS swap decode prefill
權重留 page cache(MEMWATCH_SWEEP=0) 0.384 GiB 0 2.67 tok/s 7.72 tok/s
權重頁全丟回 SSD(MEMWATCH_SWEEP=1) 0.226 GiB 0 0.12 tok/s 0.94 tok/s

為了一個模型大小零頭的記憶體差異(0.16 GiB),全丟 SSD 要賠上 22 倍的 decode 速度。 背景 sweeper(patch 0001)才是關鍵機制,不是「把權重丟掉」。

對照組:不釋放權重頁(LLAMA_MMAP_RELEASE=0)的兩個配置峰值都是 2.78 GiB = 整份權重常駐,直接違規 —— 這證明 sweeper 是必要的,不是可有可無的調校。

三個量測陷阱(全部都踩過,也全部修好了)

這個專案最大的風險不是記憶體不夠,是量測看起來通過、實際沒通過:

  1. mmap 的權重頁會算進 RSS,而且真的佔用系統 RAM。 只算匿名記憶體會得到 「0.21 GiB ✓」這種假通過(總 RSS 其實是 2.78 GiB)。→ 一律用 peak_rss_gb。
  2. bench.sh 判定基準寫錯過。 原本硬寫 --limit-gb 4,所以 2.78 GiB 的違規配置 也被標成 within_limit: true。→ 已改用 MEMORY_LIMIT_GB(預設 1)並輸出明確 verdict。
  3. validate/bench.jsonl 曾經被 mkdir 成目錄,導致每次結果 append 都失敗, 整個矩陣的結果靜默遺失 —— 記憶體佳的舊資料沒有留下來。 → 已修。另外 posix_fadvise sweep 打錯檔案會低估 RSS(頁沒清掉卻不記錄)。

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

patch 做什麼 環境變數
0001-ssd-mmap-release.patch 權重頁 madvise(MADV_DONTNEED),由背景 sweeper 在計算進行中定期丟回 SSD LLAMA_MMAP_RELEASE=1, LLAMA_MMAP_RELEASE_INTERVAL=20
0002-kv-ssd-pager.patch KV buffer 改用檔案 mmap,可放 SSD LLAMA_KV_SSD=auto|0|1, LLAMA_KV_SSD_MIN_MIB, LLAMA_KV_SSD_PATH

套用順序必須先 0001 再 0002(0002 的 hunk 是相對「已套 0001」的樹)。 依賴固定 commit 0504396140d1c882f5f6ee34466a42db7ae90114,master 前進後 patch 錨點會失效。

為什麼 sweeper 必須在計算進行中跑:單一 token 的 decode 會走完全部層, 峰值發生在任何「算完才釋放」之前 —— 實測放在 synchronize() 之後時峰值會衝到整個模型大小。

模型架構(實讀 GGUF metadata)

general.architecture             = qwen35
block_count                      = 32
full_attention_interval          = 4      -> 8 層 full attention, 24 層 linear/SSM
attention.head_count_kv          = 4
attention.key_length / value_length = 256
embedding_length                 = 2560
context_length                   = 262144
general.file_type                = 15 (Q4_K_M)

KV 每 token = 2 × 8 × 4 × 256 = 16384 elems:

KV 型別 每 token @1024 @262144
f16 32.0 KiB 0.031 GiB 8.000 GiB
q8_0 17.0 KiB 0.017 GiB 4.250 GiB
q4_0 9.0 KiB 0.009 GiB 2.250 GiB

→ 262144 上下文連最省的 q4_0 都超出 1 GiB 預算,必須靠 SSD-backed KV。


使用方法

想直接用 prompt 讓人(或 AI agent)從零重建整個專案 →看 **REPRODUCE.md**, 裡面有集中的變數表、驗收門檻與換模型指引。

一鍵安裝 + 驗證

git clone https://huggingface.co/HelloSun/sddqwen35-4b
cd sddqwen35-4b

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

編譯在 2 vCPU 上約 6 分鐘(-j2),模型下載約 10 秒(xet 加速)。 只做驗證約 8 分鐘(CPU 純推論)。

常用指令

./installpi.sh --validate-only    # 只重跑 1K 驗證
./installpi.sh --probe-maxctx     # 驗證 262144(⚠️ 尚未有證據,跑完請 sync)
./installpi.sh --ctx 8192         # 用任意上下文啟動

./bench.sh                        # 記憶體 / 速度取捨矩陣
./bench.sh "mycase:MEMWATCH_SWEEP=0 LLAMA_MMAP_RELEASE=1"   # 自訂單一配置

./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.5-4b-q4_k_m",
       "messages":[{"role":"user","content":"用一句話介紹你自己"}],
       "max_tokens":8}'

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

pi-local -p "你好"

建議的生產設定

MEMWATCH_SWEEP=0        # 權重留 page cache -> 快 22 倍, 仍在 1 GiB 內
LLAMA_MMAP_RELEASE=1
LLAMA_MMAP_RELEASE_INTERVAL=20
LLAMA_KV_SSD=1          # KV 一律放 SSD

關鍵環境變數

變數 預設 說明
MEMORY_LIMIT_GB 1 硬約束(總 RSS,含 page cache)
LLAMA_MMAP_RELEASE 1 權重背景釋放(核心機制,關掉就會違規)
LLAMA_MMAP_RELEASE_INTERVAL 20 sweeper 間隔 ms
LLAMA_MMAP_PREFETCH 0 不做 MAP_POPULATE,權重留在 SSD
LLAMA_KV_SSD auto KV ≥ LLAMA_KV_SSD_MIN_MIB 才放 SSD
LLAMA_KV_SSD_PATH $MODEL_DIR/kv-ssd.cache SSD KV 檔
LLAMA_REPACK 0 --no-repack,省掉權重匿名複本
LLAMA_ENABLE_AMX 0 編譯期關 AMX(會產生 13.9 GiB 權重複本)
LLAMA_THREADS 8 執行緒(越多 → 計算緩衝區越大)

效能:誠實說明

配置 峰值總 RSS decode prefill ≤1 GiB
權重留 page cache(建議) 0.384 GiB 2.67 tok/s 7.72 tok/s ✅
權重全丟 SSD 0.226 GiB 0.12 tok/s 0.94 tok/s ✅
權重常駐 RAM + KV SSD(解除限制) 2.77 GiB 2.82 tok/s 6.21 tok/s ❌

解除記憶體限制幾乎沒有速度收益:把 2.4 GiB 權重灌進 RAM 只快 6% (2.82 vs 2.67 tok/s),記憶體卻變 7.2 倍、直接超標 2.8 倍。 原因是 4B 的權重只有 2.4 GiB,SSD 讀取速度遠高於計算需求 —— 這台機器的瓶頸是 CPU 算力,不是 I/O。 (在 sddqwen38-27b 結論相反:15.33 GiB 權重才讓 I/O 成為瓶頸。)

另外兩個實測發現:KV 放 SSD 反而比放 RAM 快(2.82 vs 2.47 tok/s); 執行緒開越多越慢(2 threads 2.82 → 4 threads 1.74 → 8 threads 0.32), 因為本機只有 2 vCPU。LLAMA_THREADS 請設成 nproc 或更少,預設的 8 在小機器上是錯的。

tok/s 是「模型 × 機器 × 配置」的性質,不是模型的性質。 上表是 2 threads 的數字;舊機器 8 threads 量到 7.50 tok/s。換機器請重跑 ./bench.sh。 因為權重要從 SSD 讀,這台磁碟的讀取速度直接決定結果 —— 程式碼不需要改,只會變快。

證據

檔案 內容
validate/validation.json 1K 驗證報告,all_passed: true
validate/memwatch-ctx1024.json 1K 峰值總 RSS / swap / 實際生效的強制手段
validate/chat-ctx1024.json 1K 實際推論輸出與 timings
validate/bench.jsonl 記憶體 / 速度取捨矩陣(含 verdict 判定欄位)
validate/bench-logs/ 每個配置的完整 memwatch + 推論記錄
validate/bench-nolimit.jsonl 解除記憶體限制的實測(權重常駐 RAM)
validate/bench-logs-nolimit/ 上面那批的原始 memwatch + 推論記錄
STATUS.md 進度真相來源(含所有技術決定的理由與矩陣)
AGENTS.md 續作指引 + 已知的坑(接手前必讀)
REPRODUCE.md 可復現本專案的完整 prompt(自我足夠,含換模型指引)

接手前請先讀

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

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

下一步(見 STATUS.md):跑 --probe-maxctx 補上 262144 的證據, 並驗證建議的新生產設定在 262144 下是否仍合規。

參考資料(未修改)

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