@tom_doerr: 在 16GB 内存 Mac 上运行 35B 模型 https://github.com/walter-grace/mac-code…
摘要
该工具支持通过从 SSD 流式加载模型权重,在 16GB Mac 上运行 Qwen3.5-35B 等大型语言模型,经优化配置后最高可达 30 tok/s。
查看缓存全文
缓存时间: 2026/05/11 18:52
可在 16GB 内存的 Mac 上运行 35B 参数模型 https://t.co/LXrWOHZthu https://t.co/OXxdMFStfo — # walter-grace/mac-code 来源:https://github.com/walter-grace/mac-code # mac code **在 Mac 上运行无法装入内存的模型。每月 0。** ## 我可以在我的 Mac 上运行吗? | 您的 Mac | 内存 | 可运行的内容 | 速度 | |----------|-----|-----------------|-------| | 任意 Mac | 8 GB | Qwen3.5-9B (Q4_K_M, 5.3 GB),4K 上下文 | 16-20 词/秒 | | 任意 Mac | 16 GB | Qwen3.5-9B (Q4_K_M, 5.3 GB),64K 上下文 | 16-20 词/秒 | | **Mac mini M4** | **16 GB** | **Qwen3.5-35B-A3B (IQ2_M, 10.6 GB)** | **30 词/秒** | | **Mac mini M4** | **16 GB** | **Qwen3-30B-A3B Q4 (17.2 GB) via Expert Sniper** | **4.3 词/秒** | | **Mac mini M4** | **16 GB** | **Qwen3.5-35B-A3B Q4 (19.5 GB) via Expert Sniper** | **5.4 词/秒** | | Mac mini M4 | 16 GB | Qwen3.5-35B-A3B Q4_K_M (22 GB) via Flash Streaming | 1.54 词/秒 | | Mac mini M4 | 16 GB | Qwen3.5-27B (16.1 GB) via Flash Streaming | 0.18 词/秒 | | Mac mini M4 Pro | 48 GB | 35B at full Q4 in RAM | 30+ 词/秒 | > **“我想在我的 16GB M2 上运行 Qwen 27B 但失败了。这不可能吧?”** > > 这是可能的。我们从 SSD 流式传输 FFN 权重——仅有 5.5 GB 驻留在内存中。输出连贯,全 4-bit 质量。虽然速度慢(Mac mini M4 上为 0.18 词/秒),但该方法适用于任何 16 GB Apple Silicon Mac。无需 2-bit 压缩,无 mmap 抖动,无交换空间耗尽崩溃。[查看工作原理](#how-flash-streaming-works) --- ## 快速开始 ### 35B 代理(推荐 —— 16 GB 下 30 词/秒)最快的选项。使用 llama.cpp 配合完全装入内存的 2-bit 量化 (IQ2_M)。 ``bash brew install llama.cpp pip3 install rich ddgs --break-system-packages # 下载模型 (10.6 GB) python3 -c " from huggingface_hub import hf_hub_download hf_hub_download('unsloth/Qwen3.5-35B-A3B-GGUF', 'Qwen3.5-35B-A3B-UD-IQ2_M.gguf', local_dir='HOME/models/’) “ # 启动服务器 + 代理 llama-server \ –model ~/models/Qwen3.5-35B-A3B-UD-IQ2_M.gguf \ –port 8000 –host 127.0.0.1 \ –flash-attn on –ctx-size 12288 \ –cache-type-k q4_0 –cache-type-v q4_0 \ –n-gpu-layers 99 –reasoning off -np 1 -t 4 python3 agent.py ### 9B 支持 64K 上下文bash python3 -c “ from huggingface_hub import hf_hub_download hf_hub_download(‘unsloth/Qwen3.5-9B-GGUF’, ‘Qwen3.5-9B-Q4_K_M.gguf’, local_dir=‘HOME/models/') " llama-server \ --model ~/models/Qwen3.5-9B-Q4_K_M.gguf \ --port 8000 --host 127.0.0.1 \ --flash-attn on --ctx-size 65536 \ --cache-type-k q4_0 --cache-type-v q4_0 \ --n-gpu-layers 99 --reasoning off -t 4 python3 agent.py `` --- ## Flash 流式传输 以完整质量运行**确实无法装入内存**的模型。无需 2-bit 压缩。无 mmap 抖动。 ### 实测结果 下方的所有数字均在 16 GB Mac mini M4 上测量。非估算值。 | 模型 | 总大小 | 内存占用 | 速度 | 质量 | |-------|-----------|---------|-------|---------| | Qwen3-32B (dense) | 18.4 GB | 4.5 GB | 0.15 词/秒 | 全 4-bit | | **Qwen3.5-27B (dense hybrid)** | **16.1 GB** | **5.5 GB** | **0.18 词/秒** | **全 4-bit** | | Qwen3-30B-A3B (MoE) | 17.2 GB | 8.7 GB | 4.3 词/秒 | 全 4-bit | | Qwen3.5-35B-A3B (MoE) | 19.5 GB | 8.7 GB | 5.4 词/秒 | 全 4-bit | 所有数字均在 16 GB M4 Mac Mini 上针对冷启动下的 5 个不同提示词测量。质量已验证(Canberra 数据集,8.3066 分,正确的 Python 代码等)。两款模型均使用:感知缓存的路由偏置 (1.0) + 共激活预取 + 合适大小的 LRU 缓存。bias=1.0 是通用的安全最大值——两款模型在 1.5 时质量都会下降。 ### 磁盘需求 | 模型 | 处理后大小 | 所需可用磁盘空间 | |-------|---------------|-----------------| | 30B / Coder-30B | 17 GB | ~20 GB | | 35B | 19 GB | ~25 GB | | 80B | 43 GB | ~50 GB | | 122B | ~65 GB | ~70 GB | | 235B | ~130 GB | ~140 GB | 对于更大的模型,请使用外部 NVMe 驱动器: ``bash mlx-sniper download qwen3-next-80b -o /Volumes/MySSD/qwen3-next-80b mlx-sniper run /Volumes/MySSD/qwen3-next-80b -p "hello" -v `` ### Flash 流式传输的工作原理 按访问模式拆分模型: **固定在内存中(4-6 GB):** 注意力权重、嵌入、归一化层、KV 缓存。加载一次,永久驻留。 **每 token 从 SSD 流式传输:** FFN 权重(模型的主体)。逐层加载,用于一次矩阵乘法,随即丢弃。内存永不增长。 `` 对于每个 token: 对于每一层: 1. 运行注意力计算(来自内存 —— 瞬间完成) 2. 从 SSD 加载 FFN 权重 (~165-221 MB) 3. 在 GPU 上运行 FFN 矩阵乘法 4. 丢弃 FFN 权重 —— 内存保持平稳 `` 对于 MoE 模型,第 2 步仅加载 8 个活跃专家 (~14 MB),而非全部 256 个。这就是为什么 MoE 快 10 倍的原因。 ### 运行 35B MoE 代理(1.54 词/秒,1.42 GB 内存) 交互式代理,支持网络搜索、Shell 命令和思维链。16 GB Mac 上的 22 GB 模型。 **需要预构建的流式文件** —— 参见 [`research/flash-streaming/`](research/flash-streaming/) 获取拆分/重建工具。 ``bash cd research/flash-streaming python3 moe_agent.py `` ### 在 16 GB 上运行 27B Dense(0.18 词/秒,5.5 GB 内存) ``bash cd research/flash-streaming pip3 install mlx-lm transformers --break-system-packages # 一次性操作:下载模型 (~16 GB) 并拆分以便流式传输 python3 -c " from huggingface_hub import snapshot_download snapshot_download('mlx-community/Qwen3.5-27B-4bit', local_dir='HOME/models/qwen35-27b-mlx-4bit’) “ python3 split_dense_27b.py # 运行 python3 flash_stream_27b.py ### 批处理联合专家 (Batched Union-of-Experts)(5.1 词/秒) 一个研究原型,在一次前向传播中验证 8 个 token。而不是为每个 token 单独加载专家,它计算所有 8 个 token 的活跃专家的集合并(每层约 27 个独特专家,而不是 64 个),并加载一次。bash cd research/flash-streaming python3 batched_moe.py 这是验证速度(检查草稿 token),而非生成速度。适用于推测解码。 --- ## 代理命令 | 命令 | 操作 | |---------|--------| | `/agent` | 代理模式(默认)—— 搜索、shell、聊天 | | `/raw` | 直接流式传输,无工具 | | `/search ` | 快速网络搜索 | | `/stats` | 会话统计信息 | | `/clear` | 重置对话 | | `/quit` | 退出 | --- ## 文件 ### 顶层(日常使用) | 文件 | 功能 | |------|-------------| | `agent.py` | 生产级代理 —— 通过 llama.cpp 路由到搜索/shell/chat | | `chat.py` | 带有 llama.cpp 的简单流式聊天 | | `dashboard.py` | llama.cpp 的实时监控仪表板 | | `setup.sh` | 一键安装(llama.cpp + 模型下载 + 配置) | | `config.example.json` | 示例配置 | | `web/` | Web UI (server.py + index.html) | ### MLX 后端 (`mlx/`) | 文件 | 功能 | |------|-------------| | `mlx_engine.py` | MLX 推理服务器,支持 64K 上下文和 KV 缓存持久化 | | `kv_cache.py` | KV 缓存保存/加载以实现会话持久化 | | `paged_inference.py` | 分页注意力实验 | | `turboquant.py` | TurboQuant 量化实验 | | `benchmark.py` | 基准测试工具 | ### Flash 流式传输研究 (`research/flash-streaming/`) 研究历程:我们构建、测量并迭代。每个文件代表一个步骤。 | 文件 | 功能 | 关键发现 | |------|-------------|---------------| | `flash_stream.py` | v1: 基于 mmap 的流式传输 (0.12 词/秒) | 拆分模型架构可行 | | `flash_stream_v2.py` | v2: F_NOCACHE 直接 I/O (0.15 词/秒) | 比 mmap 快 27% | | `flash_stream_27b.py` | 27B dense 流式传输 (0.18 词/秒) | 方法适用于 dense + 混合 SSM 模型 | | `flash_moe.py` | MoE 专家级流式传输引擎 | 仅从 SSD 加载活跃专家 | | `moe_agent.py` | **可用的 35B 代理** (1.54 词/秒,1.42 GB) | 16 GB Mac 上连贯的 22 GB 模型 | | `batched_moe.py` | 批处理联合专家 (5.1 词/秒) | 每层约 27 个独特专家,而非 64 个 | | `expert_io.py` | F_NOCACHE + pread 专家读取器 (8 线程) | 填满 NVMe 队列深度 | | `direct_io.py` | F_NOCACHE + pread 用于 dense FFN 层 | 绕过 macOS 统一缓冲缓存 | | `split_mlx_model.py` | 将 35B MoE 拆分为固定部分 + 每层专家 | 16KB 对齐以适配 DART IOMMU | | `split_dense_27b.py` | 将 27B dense 拆分为固定部分 + 每层 FFN | 相同技术,不同架构 | | `convert_split.py` | GGUF → 拆分 safetensors 转换 | GGUF 是列主序 | | `convert_aligned.py` | Safetensors → 16KB 对齐的二进制 | F_NOCACHE 直接 I/O 必需 | | `dequant_gguf.py` | 自定义 Q4_K/Q6_K 反量化 (numpy) | MLX 无法读取 GGUF Q4_K 块 | | `rebuild_pinned.py` | 从 MLX 黄金模型重建固定权重 | 修复 SSM 权重的 dtype 问题 | | `flash_agent.py` | 32B dense 流式传输代理(早期版本) | 概念验证 | | `flash_stream_batched.py` | 批处理评估实验 | 证明 eval 同步不是瓶颈 | | `README.md` | 详细的研究报告及所有测量数据 | 完整方法论和结果 | --- ## 关键发现 这些都是我们要付出艰辛代价才学到的东西。每项链接至发现/修复该问题的文件。 1. **GGUF 是列主序** — `flat.reshape(ne[1], ne[0])`,而非 `.reshape(ne[0], ne[1]).T`。错误的 reshape 会产生正确的形状但输出垃圾数据。(`dequant_gguf.py`, `convert_split.py`) 2. **MLX 4-bit 比预期大 15%** — group_size=64 时的 scales + biases 增加了 0.031 bytes/param。32B 模型是 18.4 GB,而非 16 GB。这就是为什么即使在 4-bit 下模型也无法装入 16 GB 内存的原因。(`research/flash-streaming/README.md`) 3. **`nn.quantize()` 静默跳过 MoE 专家** — `SwitchLinear` 不是 `nn.Linear` 的子类。您必须传递一个明确包含它的 `class_predicate`。否则,专家将以 float16 运行并产生垃圾输出。(`moe_agent.py`) 4. **`gather_qmm` 消除了累加器发散** — 8 次单独的 `quantized_matmul` 调用会在 40 层上累积舍入误差。单次批处理的 `gather_qmm` 调用与参考模型完全匹配。(`batched_moe.py`, `flash_moe.py`) 5. **F_NOCACHE 比 mmap 快 27%** — macOS 统一缓冲缓存为顺序流式工作负载添加了开销。`fcntl(F_NOCACHE)` + 具有 16KB 对齐的 `os.pread()` 可以完全绕过它。(`direct_io.py`, `expert_io.py`) 6. **在 `nn.Module` 上使用 `setattr` 会导致内存泄漏** — 通过 `setattr` 将 FFN 权重注入模型树会阻止垃圾回收。内存每 16 层增长 3.6 GB。修复:直接在加载的数组上使用 `mx.quantized_matmul`,切勿触碰模型树。(`flash_stream.py`) 7. **批处理层没有帮助** — 我们测试了 8 层批次(16 次 eval 对 128 次)。零速度提升。瓶颈是 SSD I/O,而非 GPU 同步开销。(`flash_stream_batched.py`) --- ## 架构 ┌──────────────────────────────────────────────┐ │ agent.py — 作为路由器的 LLM │ │ 搜索 / shell / chat │ ├──────────┬───────────────────────────────────┤ │ llama.cpp│ MLX 后端 │ │ (快) │ + KV 缓存保存/加载 │ │ │ + Flash 流式传输(外存计算) │ │ │ + MoE Expert Sniper (SSD) │ ├──────────┴───────────────────────────────────┤ │ Apple Silicon — 统一内存 + NVMe SSD │ └──────────────────────────────────────────────┘ `` — ## 致谢 - Qwen3.5 (https://huggingface.co/Qwen) — 模型 - llama.cpp (https://github.com/ggerganov/llama.cpp) — 推理引擎 - MLX (https://github.com/ml-explore/mlx) — Apple 的 ML 框架 - Unsloth (https://huggingface.co/unsloth) — GGUF 量化 - mlx-community (https://huggingface.co/mlx-community) — 预先转换的 MLX 模型 ## 许可证 MIT
相似文章
Show HN: 在48GB Mac上运行104GB Qwen3.8-Flash-Next,速度约12 tok/s
slotstream 是一个开源工具,它通过从SSD流式传输模型权重,使得在内存有限的Mac上运行像Qwen3.8-Flash-Next这样的大型AI模型成为可能,在48GB Mac上实现了大约12 tokens每秒的速度。
我通过从SSD流式传输MoE专家权重,在36 GB MacBook上运行35B–480B编码模型——自包含应用,并且我也公开了那些*失败*的基准测试
Slipstream 通过从SSD而非RAM流式传输MoE专家权重,使得在36 GB MacBook上运行大型编码模型(35B–480B)成为可能。基准测试显示,35B模型约13–19 tok/s,118B模型约2.8 tok/s,并诚实报告了失败的方法。
@Oluwaphilemon1:Qwen3.8-27B 正在一台仅有 16GB 统一内存的 M1 MacBook 上本地运行。不用云端 API。不用外接 GPU。只需一台……
演示中,Qwen3.8-27B 使用 UD-Q2_K_XL 量化和全量 Metal 卸载,在配备 16GB 统一内存的 M1 MacBook 上本地运行,生成速度达到约 4 token/秒(使用推测解码时约为 5.5 token/秒)——这表明,大型开放权重模型可以在许多人已经拥有的硬件上运行。
Show HN:在 Mac 上仅用 4.3 GB 内存运行 80B Qwen,以及在 iPhone 上运行 35B
演示了在 Mac 上仅用 4.3 GB 内存运行 80B Qwen 模型,以及在 iPhone 上运行 35B 模型,展示了本地 LLM 推理的极致内存优化。
2台配备 512GB 内存的 M3 Ultra Mac Studio
硬件投入约 2.5 万美元。告诉我你们希望我在这两台设备上部署什么模型,我会协助测试。目前我已通过 Exo 后端跑通了 DeepSeek v3.2 Q8 版本;当前每台设备均在运行 GLM 5.1 Q4(正在排查为何 Exo 无法加载 Q8 版本)。静候社区完成 Kimi 2.6 针对 MLX/mmap 的优化适配。