File size: 7,706 Bytes
ae9bb4c
 
 
 
 
 
 
 
 
 
 
 
446c618
ae9bb4c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
446c618
 
 
 
 
 
ae9bb4c
 
446c618
 
 
 
ae9bb4c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# 硬體與執行環境(實測)

這個 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. 重現這些量測

```bash
# 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
```