Download HARDWARE.md from HelloSun/deepseekv4flashgguf_EPQ: direct link, hf CLI and curl.
- Browser
- Download file 7.71 kB
-
https://huggingface.co/HelloSun/deepseekv4flashgguf_EPQ/resolve/main/HARDWARE.md
- Command line
-
hf download hf://HelloSun/deepseekv4flashgguf_EPQ/HARDWARE.md
-
curl -L -o HARDWARE.md https://huggingface.co/HelloSun/deepseekv4flashgguf_EPQ/resolve/main/HARDWARE.md
硬體與執行環境(實測)
這個 repo 的所有數字都是在下面這台機器上量的。這裡把「量到什麼」與 「怎麼重量」都寫出來,避免出現別處那種「16 threads 是核心數嗎 / GB 還是 GiB / RAM 預算是不是硬性上限」的歧義。
0. 一句話版本
| CPU | Intel Xeon Platinum 8559C,2 socket × 48 core × 2 thread = 192 logical CPU 可見 |
| 實際算力配額 | cgroup v2 cpu.max = 1600000 100000 → 16.0 CPU |
| 實體 RAM | 2,000.0 GiB = 1.953 TiB(MemTotal 2,097,117,292 kB) |
| Swap | 1,600.0 GiB = 1.5625 TiB(SwapTotal 1,677,721,504 kB) |
| cgroup v2 記憶體上限 | memory.max = 104,000,000,000 B = 96.86 GiB / 104.0 GB |
| 實際綁住本專案的上限 | /root/memkill.py 80 2 64 → 單一子行程 64 GiB、opencode 及其全部子行程合計 80 GiB、取樣間隔 2 s |
| 儲存 | / 是 overlayfs,底層是 nvme0n1 = Amazon Elastic Block Store(網路區塊裝置,不是本地 NVMe SSD)7.8 TB |
磁碟單流(dd bs=4M oflag=direct / iflag=direct) |
寫 1.1 GB/s、讀 2.0 GB/s |
| cgroup 檔案系統 | 唯讀(/sys/fs/cgroup 寫入失敗,cgroup.subtree_control 為空) |
| ISA | avx2 / fma / f16c / avx512f,dq,bw,vl,vbmi,ifma,bf16 / avx512_vnni / amx_int8, amx_bf16, amx_tile |
1. CPU:三個數字不要搞混
這台機器上「16」這個數字有三個完全不同的來源,混用會得到錯的結論:
| 量測指令 | 結果 | 意義 |
|---|---|---|
lscpu | grep ^CPU(s) |
192 | 實體 logical CPU 數(2×48×2) |
python3 -c "len(os.sched_getaffinity(0))" |
192 | cgroup 沒有縮 CPU set |
cat /sys/fs/cgroup/cpu.max |
1600000 100000 |
配額 = 16.0 CPU(這才是真正限制你的東西) |
nproc |
16 | ⚠️ 不是因為配額或 affinity,是因為環境變數 OMP_NUM_THREADS=16 |
nproc 會尊重 OMP_NUM_THREADS,所以在這台機器上 nproc 剛好等於配額是
巧合,不是因果。hardware_concurrency() 回的是 192(實體數),
sched_getaffinity() 回的也是 192(沒有縮)—— 所以「容器裡
hardware_concurrency() 會回宿主核心數」這句話在這台機器上無法用觀察區分
「回實體數」和「回 affinity 數」,因為兩者剛好都是 192。
**本專案的實驗一律用 -t 16**(= 配額),n_threads_batch 相同。
-t 超過 16 只會讓執行緒排程互相搶時間,不會變快。
2. 記憶體:三個不同的上限
| 機制 | 值 | 是硬限制嗎 | 誰在用 |
|---|---|---|---|
cgroup v2 memory.max |
96.86 GiB / 104.0 GB | 是(OOM kill) |
整個容器 |
memkill.py 單一子行程 |
64 GiB | 是(SIGKILL,連同子樹) |
每個 sdq-chat |
memkill.py 合計(opencode + 全部子行程) |
80 GiB | 是(SIGKILL 最大的那個子樹) |
整個工作階段 |
--ram-budget-mb(SDQ pager budget) |
4/8/16/32 GiB | 不是,只是算 arena 大小用的輸入 | 見下 |
memkill.py 的實際行為(讀 /root/memkill.py 確認):
每 2 秒掃一次 /proc,把 comm 以 opencode 開頭的行程當 root,
把 root 的所有後代加總 RSS;超過 80 GiB 就挑記憶體最大的那個直接子行程
連同它的子樹 SIGKILL;任何單一行程超過 64 GiB 也直接殺。
opencode 本體永遠不會被殺。
⚠️ 這意味著
peak RSS的天花板是 64 GiB,不是 cgroup 的 96.86 GiB。 這也是為什麼這個 repo 沒有做「原生路徑 vs 分頁路徑」的端到端 A/B: 原生路徑要把 127.27 GiB 的權重載入 RSS,一定會被這支守衛砍掉。
為什麼 RAM 實驗只能是「pager budget」
/sys/fs/cgroup 是唯讀(echo 0 > memory.high 直接 Read-only file system),
cgroup.subtree_control 是空的,所以沒辦法在這個容器裡為 4/8/16/32 GiB
各建一個獨立的 cgroup 限額。
因此 results/memory_levels_dsv4-4-8-16-32g.md 的四列是「把 RAM 預算交給
pager 去算 arena 大小」再量 peak RSS,不是四個各自受限的容器。
各列的實際 peak RSS 都如實列出(2233 / 6325 / 14508 / 30894 MB),可以直接
對照 memory.max,四列都沒有接近任何上限。
3. 儲存:這不是「SSD」
$ stat -fc %T /root → overlayfs
$ lsblk -o NAME,TYPE,SIZE,ROTA,MODEL # 2026-10-12 實測
nvme0n1 disk 7.8T 0 Amazon Elastic Block Store ← / 在這上面
nvme1n1 disk 3.5T 0 Amazon EC2 NVMe Instance Storage
nvme2n1 disk 3.5T 0 Amazon EC2 NVMe Instance Storage
nvme3n1 disk 3.5T 0 Amazon EC2 NVMe Instance Storage
nvme4n1 disk 3.5T 0 Amazon EC2 NVMe Instance Storage
附帶 instance storage 的磁碟編號會隨開機/熱插拔變動(nvme1n1–nvme4n1 之間, 這台機器上是 1、2、3、4 都在,
md0是其中的 RAID0)。只有nvme0n1對本專案 有意義,因為它才是/(overlayfs)底下的那一顆;其餘都是 instance storage。
/ 是 overlayfs,底層是 Amazon Elastic Block Store(EBS)——
那是網路-attached 區塊裝置,不是本地 NVMe SSD。
把指標命名為 SSD MiB/token 在這個環境下是有誤導性的:
它量到的是「這個容器讀檔案的速率」,不是 SSD 控制器速率。
本機實測單流(bs=4M):
寫 dd if=/dev/zero of=.ddtest bs=4M count=512 oflag=direct → 1.1 GB/s
讀 dd if=.ddtest of=/dev/null bs=4M iflag=direct → 2.0 GB/s
pager 用的是 O_DIRECT(SDQ_FADV_DONTNEED=1 + 每個 shard 各開一組 O_DIRECT
fd),所以實測的 2.0 GB/s 是它真正能吃到的上限。--stats-file 報的
SSD MiB/tok 要拿這個數字去對照,不是拿消費級 SSD 的 7 GB/s 去對照。
4. ISA 與編譯
CPU 有 amx_int8 / amx_bf16 / amx_tile / avx512_vnni,但這個專案刻意
不使用 AMX,原因見 README 第 5 節 (e)(會讓 deepseek4 的 attn_wo_a 撞
sched assert)。所以:
-march=native會定義__AMX_INT8__→ llama.cpp 註冊 AMX extra buffer type → 踩到那個 assert。不要用-march=native。- 正確做法是明確列 ISA(README 4.2),AMX 的 CMake 選項維持預設 OFF。
- 三個 x86 基線(AVX2 / AVX512+VNNI+BF16 / native+
-mno-amx-*)的 decode tok/s 在同一台機器上量不出差異(各自 3 輪落在 1.03–1.71 這個區間裡)。
K-quant / IQ3_S / MXFP4 的點積走的是 ggml_vec_dot_* 的 AVX2 / AVX-512-VNNI
路徑,不是 AMX。
5. 重現這些量測
# CPU / 配額
lscpu | grep -E '^CPU\(s\)|^Model name|^Thread|^Core|^Socket'
python3 -c "import os; print('affinity', len(os.sched_getaffinity(0)), 'cpu_count', os.cpu_count())"
nproc; echo "OMP_NUM_THREADS=$OMP_NUM_THREADS"
cat /sys/fs/cgroup/cpu.max
# 記憶體 / cgroup
grep -E 'MemTotal|SwapTotal' /proc/meminfo
for f in memory.max memory.high memory.swap.max; do printf '%s = %s\n' "$f" "$(cat /sys/fs/cgroup/$f)"; done
stat -fc %T /sys/fs/cgroup; cat /sys/fs/cgroup/cgroup.subtree_control
( echo 0 > /sys/fs/cgroup/memory.high ) 2>&1 | head -1 # 預期 Read-only
# 守衛
tr '\0' ' ' < /proc/$(pgrep -f memkill.py | head -1)/cmdline; echo
head -25 /root/memkill.py
# 儲存
stat -fc %T /root ; lsblk -o NAME,TYPE,SIZE,ROTA,MODEL
dd if=/dev/zero of=/tmp/.ddtest bs=4M count=512 oflag=direct 2>&1 | tail -1
dd if=/tmp/.ddtest of=/dev/null bs=4M iflag=direct 2>&1 | tail -1; rm -f /tmp/.ddtest
# ISA
lscpu | grep -oE '\b(avx512_vnni|amx_int8|amx_bf16|amx_tile|avx512f|avx2)\b' | sort -u