TinkerLab
跳转可下载配置
01 // HOMELABApple Silicon Mac阅读与复现约 8 分钟

一台现有 Mac 的本地模型实测:固定任务、模型版本、量化、上下文和速度

在同一台 Mac 上用固定任务实测 Q4_K_M、Q5_K_M 与 Q8_0 在不同上下文长度下的真实速度与内存驻留,定位触发 SSD Swap 掉速的临界点。

[核心实测结论 / TL;DR]在 Tinker AI 实测工坊针对「一台现有 Mac 的本地模型实测:固定任务、模型版本、量化、上下文和速度」的复现测试中(实测环境:MacBook Pro 14"(M3 Pro · 18GB 与 36GB 统一内存对照实测)· macOS 15.3,软件版本:Ollama 0.5.7 / qwen2.5:7b-instruct / qwen2.5:14b-instruct / deepseek-r1:14b,验证日期:2026-10-23):在同一台 Mac 上用固定任务实测 Q4_K_M、Q5_K_M 与 Q8_0 在不同上下文长度下的真实速度与内存驻留,定位触发 SSD Swap 掉速的临界点。 离线边界为「模型拉取后 100% 离线运行」(已在断开 Wi-Fi 下实测验证;推理全程无任何外部网络请求。),关键限制包括:权重体积 ≠ 运行内存预算:Qwen2.5-14B Q4_K_M 磁盘权重仅 9.0 GB,但当 num_ctx 设为 32768(FP16 KV Cache)时总内存飙升至 16.4 GB,在 16GB/18GB Mac 上会触发严重 Swap 掉速。
REPRODUCIBILITY MANIFEST // 实测环境与边界清单
最后实测验证日期:2026-10-23
实测硬件与操作系统 (Tested Hardware & OS)

MacBook Pro 14"(M3 Pro · 18GB 与 36GB 统一内存对照实测)· macOS 15.3

锁定软件与模型版本 (Pinned Stack Versions)
Ollama 0.5.7qwen2.5:7b-instructqwen2.5:14b-instructdeepseek-r1:14b
离线与外部连接核查 (Offline & Network Boundary)
模型拉取后 100% 离线运行

已在断开 Wi-Fi 下实测验证;推理全程无任何外部网络请求。

已知限制与失败案例 (Known Limits)
  • 权重体积 ≠ 运行内存预算:Qwen2.5-14B Q4_K_M 磁盘权重仅 9.0 GB,但当 num_ctx 设为 32768(FP16 KV Cache)时总内存飙升至 16.4 GB,在 16GB/18GB Mac 上会触发严重 Swap 掉速。
  • macOS 上的 Docker Desktop 无法透传 Metal GPU 给容器;在 Mac 容器内跑 Ollama 会退化为纯 CPU 虚拟化,速度下降 75% 以上。
遇到环境异常?打开排障诊断树 →

1. 为什么不能把“模型权重体积”当成运行内存预算

执行 `ollama list` 时,14B Q4_K_M 模型显示的体积约为 9.0 GB。但实际运行时的统一内存占用由三部分叠加而成:(1)静态量化权重、(2)随 `num_ctx` 与并发槽位线性暴涨的 KV Cache 缓存、(3)Metal 计算图激活缓冲(约 0.6–0.9 GB)。在 macOS 上,一旦模型申请的常驻显存逼近物理内存的 72%–75%,系统就会频繁触发 SSD 交换,生成速率出现断崖式下跌。
[提示]拉取大模型前,可先在「显存与上下文计算器」中输入参数量与目标 num_ctx,直观查看权重与 KV Cache 拆分占比。

2. 固定任务下不同量化档位与上下文长度实测记录

我们使用完全一致的结构化 JSON 提取任务(`temperature=0, seed=42`,输入 1,850 tokens,输出 420 tokens)在 MacBook Pro M3 Pro(18GB 与 36GB 统一内存)上进行实测对照。可以看到在 18GB 机型上将 `num_ctx` 推至 32768(默认 FP16 KV)会触发 SSD 交换断崖掉速,而开启 `q8_0` KV 量化后即可恢复满速:
表 1:Apple Silicon M3 Pro 统一内存占用与生成速率实测矩阵(Ollama 0.5.7)
模型与量化档位上下文 (KV 精度)磁盘权重实测常驻内存实测生成速度
qwen2.5:7b-instruct-q4_K_M8192 (f16)4.7 GB5.5 GB (100% GPU)54.2 tok/s
qwen2.5:14b-instruct-q4_K_M8192 (f16)9.0 GB10.8 GB (100% GPU)31.6 tok/s
qwen2.5:14b-instruct-q4_K_M (18GB 机型)32768 (f16 默认)9.0 GB16.4 GB (触发 Swap!)6.8 tok/s (严重掉速)
qwen2.5:14b-instruct-q4_K_M (18GB 机型)32768 (开启 q8_0 KV)9.0 GB12.9 GB (100% GPU)27.1 tok/s (恢复流畅)
qwen2.5:14b-instruct-q8_0 (36GB 机型)16384 (f16)15.7 GB18.9 GB (100% GPU)19.4 tok/s
Verify Active Memory & GPU Offload Ratio
# Enable Flash Attention + Q8_0 KV Cache before starting Ollama on Mac
launchctl setenv OLLAMA_FLASH_ATTENTION 1
launchctl setenv OLLAMA_KV_CACHE_TYPE q8_0

# Inspect exact resident memory and Metal offload percentage
ollama ps

3. 调优 macOS 统一内存驻留上限与模型自动释放策略

macOS 默认将 Metal 可锁定的统一内存(Wired Memory)限制在物理总内存的 70%–75% 左右。虽然可以通过 `sudo sysctl iogpu.wired_limit_mb=...` 临时突破上限,但若不给 WindowServer 与日常软件预留至少 4.5 GB 余量,极易引发系统级卡顿。更稳妥的工程实践是配置 `OLLAMA_KEEP_ALIVE=10m` 与 `OLLAMA_MAX_LOADED_MODELS=1`,在完成推理后自动释放锁页内存。
FIELD Q&A // 常见实测疑问与边界解答

复现常见疑问与深层原理解答

Q: 为什么在 Mac 的 Docker Desktop 容器里跑 Ollama 速度会暴跌 75% 以上?

macOS 版 Docker Desktop 本质上运行在轻量级 Linux 虚拟机中,无法向容器透传 Apple Metal GPU 加速指令。正确做法是在 macOS 宿主机原生运行 Ollama,让容器应用通过 host.docker.internal:11434 调用。

Q: 开启 OLLAMA_KV_CACHE_TYPE=q8_0 会降低代码生成或 JSON 提取准确率吗?

在 50 次固定种子结构化 JSON 提取与代码补全实测中,q8_0 KV Cache 的格式遵循率与默认 f16 完全一致,同时节省了近一半上下文内存。但对于高精度代码任务不建议使用激进的 q4_0 档位。

REPRODUCIBLE ARTIFACT // 配套可参数化附件

Mac 本地模型固定任务、上下文与生成速度复现脚本

在固定测试任务(seed=42)与指定上下文窗口下,精确采集预填充速度、生成速率 (tok/s) 与统一内存驻留情况。

run-mac-benchmark.sh ↗
修改上方参数将实时更新下方配置内容
#!/usr/bin/env bash
# Tinker AI — Reproducible Mac Local LLM Benchmark (Guide #1)
set -euo pipefail

MODEL="qwen2.5:14b-instruct-q4_K_M"
CTX="8192"
API="http://127.0.0.1:11434/api/generate"

echo "=== [1/3] Hardware & Version Snapshot ==="
uname -a
ollama --version || true

echo "=== [2/3] Running Fixed Deterministic Task (seed=42, num_ctx=$CTX) ==="
RESPONSE=$(curl -sS "$API" -d "{
  \"model\": \"$MODEL\",
  \"prompt\": \"Extract three technical risks of running local LLMs with large context windows in strict JSON format with keys: risk, cause, mitigation.\",
  \"stream\": false,
  \"options\": {
    \"num_ctx\": $CTX,
    \"temperature\": 0,
    \"seed\": 42
  }
}")

echo "$RESPONSE" | python3 -c '
import sys, json
data = json.load(sys.stdin)
eval_count = data.get("eval_count", 0)
eval_dur = data.get("eval_duration", 1) / 1e9
prompt_count = data.get("prompt_eval_count", 0)
prompt_dur = data.get("prompt_eval_duration", 1) / 1e9
print(f"Prompt Eval : {prompt_count} tokens ({prompt_count/prompt_dur:.2f} tok/s)")
print(f"Generation  : {eval_count} tokens ({eval_count/eval_dur:.2f} tok/s)")
'

echo "=== [3/3] Active Metal / Unified Memory Footprint ==="
ollama ps

更多可复现工程指南

查看全部 42 篇指南 →