跳到主内容
快讯直播
AI智模界
教程

笔记本拿 SSD 当显存跑 700B GLM:分片、预读与吞吐调优

这篇能做出什么

读完你能在一台没有独立显卡的笔记本上,把数百 GB 的 GLM 超大 MoE 权重真正跑起来:能出词、能对话、能起一个本地 API。同时你会掌握一套可复用的判断方法——用 iostat、llama-bench 和几条内核参数,判断当前速度是被磁盘、内存还是 CPU 卡住,以及每一项该往哪个方向调。

先说清楚这条路线的本质:它不制造显存,只是把 SSD 当成一层很慢、但容量很大的缓存。权重的真实存放介质始终是 NVMe 固态盘,内存和 page cache 决定有多少权重能"刚好留在手边"。所以速度差异可以非常大,冷启动和热缓存的差距常常是数量级的。

这条路能成立,靠三个前提:模型是 MoE 架构(每个 token 只激活一小部分专家)、权重做了低比特量化(体积压到原来的四分之一左右)、推理运行时用 mmap 按需从文件读页。三者缺一个,笔记本就基本没有可行性。

前置条件清单

  • 笔记本:x86_64 或 Apple Silicon;内存 16 GB 起步,32–64 GB 会舒服很多;CPU 物理核心数越多越好。
  • 存储:内置 NVMe SSD,剩余空间至少是权重大小的 1.2 倍。外置 USB 硬盘盒、读卡器、机械盘都不适合这条路线。
  • 模型:GLM 系列的超大 MoE 权重,量化后体积在数百 GB 量级(按 4.5 bit/权重粗算,700B 参数约 390 GB 左右,以量化后的实际文件大小为准)。具体模型名、许可、量化版本以官方模型页为准。
  • 软件:Linux(原生或 WSL2);Python 3、git、cmake、C++ 工具链;以及一个支持 mmap 推理和 MoE 的运行时,下面以 llama.cpp 这条线为例。
  • 心态:这是一次"能跑通"的验证,不是拿来做生产推理的。首次跑通建议留出半天。

第一步:环境自检

先确认盘和内存的真实情况,避免后面白折腾。

```bash

uname -m

nproc

lscpu | grep -E 'Model name|Core\(s\)|Socket'

free -h

lsblk -d -o NAME,ROTA,SIZE,MODEL

df -h /mnt/big

```

ROTA 是 0 表示非旋转介质(SSD/NVMe)。如果模型打算放在 WSL2 里,注意别放在 /mnt/c —— 那是 9p 文件系统,顺序读性能会明显下降,要把虚拟磁盘放在 Linux 侧,或者用 wsl --mount 把 NVMe 直通给发行版。

再给盘做一次基线测量,这个数字后面要反复用到:

```bash

fio --name=seqread --rw=read --bs=1M --size=8G --direct=1 \

--filename=/mnt/big/fiotest.bin

fio --name=randread --rw=randread --bs=4k --size=2G --direct=1 \

--filename=/mnt/big/fiotest.bin

rm /mnt/big/fiotest.bin

```

记下顺序读的 GB/s。无 DRAM 缓存的 QLC 盘,或者盘快写满的时候,这个数字会掉得很难看,而它直接决定你的 token/s 上限。

第二步:拿到量化的分片权重

优先直接获取官方或社区已经量化好的 GGUF 分片,这是省时间的做法。自己从头量化 700B 权重需要先转出 bf16 中间文件(约 2 字节/参数,1.4 TB 量级),笔记本几乎不可能同时容纳中间文件和成品,除非你分批评审、边转边删。

如果确实要自己转,工具名在不同版本里可能叫 convert_hf_to_gguf.py 或 convert.py,量化工具可能叫 llama-quantize 或 quantize,以仓库当前文档为准:

```bash

python convert_hf_to_gguf.py /models/glm-xxx \

--outtype bf16 \

--outfile /mnt/big/glm-bf16.gguf

./build/bin/llama-quantize /mnt/big/glm-bf16.gguf \

/mnt/big/glm-Q4_K_M.gguf Q4_K_M

```

量化位数的取舍:比特越低体积越小、能缓存的专家越多,但质量下降。Q4 系列通常是这条路线上的平衡点,具体选哪个量化类型,看官方模型卡给出的推荐。

第三步:把权重切成可流式的分片

单个几百 GB 的文件并非不能用,但切成 10–20 GB 一段有明显好处:便于校验和断点续传、便于分盘放置、也方便用 vmtouch 观察哪一段被缓存在内存里。

工具名在不同版本里可能是 llama-gguf-split 或 gguf-split,以 build/bin 下的实际文件名为准:

```bash

./build/bin/llama-gguf-split --split --split-max-size 20G \

/mnt/big/glm-Q4_K_M.gguf \

/mnt/big/glm-split

```

产出会形如 glm-split-00001-of-000xx.gguf。加载时只需指定第一个分片,运行时会自动把后续分片串起来。

关于"权重怎么切",还有一层含义:MoE 模型里真正占体积的是专家权重,而每 token 只用其中一小撮,注意力层、embedding、router 这些"每 token 必读"的部分相对小得多。所以调优的思路是——让必读的部分尽量常驻内存,让专家部分靠缓存命中率取胜。

第四步:文件系统与挂载

用原生 Linux 文件系统(ext4、xfs),不要用 NTFS-3G 或 exFAT 承载这种超大文件,它们的映射和元数据开销会让随机读雪上加霜。挂载时关掉 atime 更新:

```ini

/etc/fstab

UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx /mnt/big ext4 defaults,noatime 0 2

```

不要把模型和正在编译的代码、数据库、下载任务放在同一块盘上。也不要让 SSD 长期处于 90% 以上占用,空闲块不足时主控的垃圾回收会明显拖慢持续读。

第五步:第一次跑通(最小命令)

先跑一个不会让你等太久的版本:小上下文、少量输出、默认 mmap。

```bash

./build/bin/llama-cli \

-m /mnt/big/glm-split-00001-of-000xx.gguf \

-c 512 \

-n 32 \

-t 8 \

-p "你好,简单介绍一下你自己"

```

-c 是上下文长度,-n 是生成 token 上限,-t 是线程数(先按物理核心数填)。这一步的意义是确认权重能被正确加载、模型能正常出词,而不是看速度。

注意在这个阶段不要加 --no-mmap。它的含义是"把全部权重一次性读进内存",只有在内存大于权重体积时才成立;笔记本上加它大概率触发 OOM 或疯狂换页。

第六步:预读与缓存管理

第一次生成会非常慢,因为每个专家都要从盘上现读。第二次、第三次会快一些,因为内核的 page cache 已经装下了一部分。想主动控制这个过程:

```bash

查看某个文件有多少比例已在 page cache 中

vmtouch /mnt/big/glm-split-*.gguf

主动把文件读进缓存(预热)

vmtouch -t /mnt/big/glm-split-*.gguf

```

vmtouch -l 可以尝试把页锁在内存里不被回收,但受 ulimit -l 限制,几百 GB 的权重在笔记本上不可能全锁住,通常只能锁住其中一部分。

如果内存紧张,可以在启动时加 --mlock(同一版本可能写作别的名字,用 --help 确认),把能锁的部分锁住,减少关键页被换出的概率。

顺手清一次冷缓存,方便做对照实验:

```bash

sync && echo 3 | sudo tee /proc/sys/vm/drop_caches

```

第七步:内存与 IO 的平衡

这一节是整条路线上收益比较高的部分。

```bash

降低匿名页被换出的倾向,把内存留给 page cache

sudo sysctl -w vm.swappiness=10

查看内存与缓存现状

grep -E 'MemTotal|MemAvailable|Cached|SwapCached' /proc/meminfo

```

几个具体做法:

  • 关掉浏览器、IDE、Docker 等吃内存的进程。它们在抢的正是模型要用的 page cache。
  • 尽量不挂 swap,或者只挂一个很小的 swap。swap 和模型 IO 落在同一块盘上,会互相拖累。
  • 如果版本支持,把 MoE 专家层显式留在 CPU、其余部分交给加速器(开关名字在不同版本里不同,用 --help | grep -i moe 查)。无独显时这个开关意义不大。
  • 集成显卡可以走 Vulkan 后端分担一点计算,但 -ngl 开的层数会占用系统内存,等于从 page cache 里抠走一块。内存吃紧时,不开反而更稳。

第八步:吞吐调优清单

按收益从高到低试:

  • -t:填物理核心数。填成超线程数往往更慢。
  • -c:上下文长度按需要给。KV cache 吃的是内存,而内存就是缓存命中率。
  • --cache-type-k / --cache-type-v:把 KV cache 量化(如 q8_0),省下的内存留给权重。
  • -b / --ubatch-size:CPU 推理时,较小的微批有时反而更快,值得试两三个值。
  • --flash-attn:支持的版本里开启。
  • --no-warmup:跳过启动预热。测冷启动时反而要去掉它。

第九步:测量,并算出你机器的上限

用自带的基准工具跑,别靠体感:

```bash

./build/bin/llama-bench \

-m /mnt/big/glm-split-00001-of-000xx.gguf \

-p 64 -n 32 -r 3 -t 8

```

同时开一个窗口看盘和 CPU:

```bash

iostat -x 1 /dev/nvme0n1

pidstat -r -d 1

```

判断瓶颈的规则很直接:

现象瓶颈方向
磁盘读带宽持续跑满IO减少每 token 读取量(更低比特量化、更小上下文),或加大内存
磁盘空闲,单核跑满计算/调度调 -t、--ubatch-size,换量化类型
磁盘和 CPU 都不满,swap 在涨内存关掉占用内存的进程,降低 swappiness

想预判能跑多快,用这个估算式:

```

每 token 需从盘读入的字节 ≈ 每 token 激活的专家个数 × 单专家体积 × 层数 × (1 - 缓存命中率)

理论上限 tok/s ≈ 实测顺序读带宽 ÷ 每 token 字节数

```

举例演算一下(下面是假设值,不是任何机器的跑分):假设 60 层,每层每 token 激活 8 个专家,单个专家 Q4 量化后约 40 MB,那么冷缓存下每 token 要从盘上读 60 × 8 × 40 MB ≈ 19 GB。按顺序读 3 GB/s 算,上限约 0.16 token/s —— 这个数字说明为什么冷启动几乎不可用。而如果内存能装下其中九成专家,读取量降到不到 2 GB,理论上限就进入每秒几个 token 的量级。

所以"无 GPU 笔记本能跑到什么速度"这个问题,答案完全取决于内存除以权重体积的比值。在 32–64 GB 内存、PCIe 4.0 NVMe 的笔记本上,实际观察到的通常是:冷缓存首次生成远低于 1 token/s,热缓存稳定后落到亚 token 级到个位数 token 级之间。这只是一个量级参考,不是跑分,你机器上的数字以 llama-bench 的输出为准。

常见坑与排错

  • 越跑越慢:盘写满了。腾出至少 20% 空间再做测试。
  • 热缓存后突然掉速:模型文件被其他进程挤出了 page cache。关掉后台大内存程序。
  • NVMe 盘很烫:长时间持续读会触发降频,用 nvme smart-log /dev/nvme0 看温度,垫高笔记本或加散热。
  • 加载就报 mmap 失败:文件系统或路径不支持 mmap,检查是不是放在了网络盘或 9p 挂载上。
  • 加了 --no-mmap 后机器卡死:内存装不下全量权重,退回默认的 mmap。
  • 首次运行慢到以为死机:正常。让输出 -n 小一点先看能不能出词。
  • 分片加载报错:确认指定的是 -00001-of- 那个文件,且所有分片在同一目录、命名连续。
  • 上下文开太大后 OOM:KV cache 也是内存。先降 -c,再考虑量化 KV cache。

下一步建议

跑通之后可以按这个顺序继续:

1. 用 llama-server 起一个本地 OpenAI 兼容接口,把这条链路接到你自己的脚本或客户端里,方便做批量测试。

2. 做一次系统的量化对比:同一个小测试集,比 Q4 与更低比特在质量与速度上的取舍。

3. 观察专家命中分布:如果某些层反复被读到,可以考虑把这几层单独拆成分片放在读性能更好的盘上。

4. 硬件侧升级的收益排序大致是:内存容量 > NVMe 顺序读带宽 > CPU 核心数。内存翻倍通常比换盘更有效。

5. 如果这变成日常工作流,认真考虑台式机或带大显存的工作站——笔记本更适合做可行性与效果验证。

一句话总结:这条路线的实质是把磁盘带宽和内存容量换算成 token 速度,你要做的所有调优,都是在提高"每 token 需要从盘上读多少字节"这个分母的命中率。

AI 生成本文由 AI 基于公开信息自动生成,仅供参考。