用 Mac mini 6 跑本地模型一晚上多少度电

我有一台 Mac mini M4 16G 日常用,另一台 Mac mini M6 32G 跑本地大模型,接雷雳线直连,我叫它 mini6。上面跑 Ollama,常用的是 qwen3.8:27b-mlx、gemma4:12b-mlx-64k、qwen2.5-coder:14b。跑得久了想知道一件事:它一天到底吃多少电。

为什么 htop 看不到功耗

我常开的监控是 htop,它给 CPU 百分比、内存、网络,没有瓦数这一栏。系统自带的活动监视器能看 GPU 占用,也不显示 GPU 此刻几瓦。搜一圈,Apple Silicon 的功耗监控基本都指向 asitop——它能看,但要 sudo。

要 sudo 不难,难的是后果:要么每次看功耗都输一遍密码,要么写个 launchd 脚本给它 root、常驻 24 小时盯着。为了看个瓦数在机器上留一个 root 进程,我不想干。

后来才明白,不是工具偷懒,是数据不在同一个地方。CPU 负载这类信息走操作系统统计(Mach host statistics),普通进程随便读,htop 拿到的就是它。GPU 几瓦、ANE 几瓦、核心频率多少,不在操作系统统计里,在 IOReport 的能量计数器和频率 residency 里;整机功耗、温度、风扇在 SMC 里。传统做法就是 asitop 那样提权去读。所以 htop 不是没做,是够不着。

siltop 是什么

Rust 写的,开源,MIT/Apache-2.0 双许可,作者 Lay Sheth,仓库 github.com/CLoaKY233/siltop,最新 v1.2.1(2026-10-16 发布)。它做的事就是把上面那套读法搬到不用 root 的路上。README 列的免 root 可读项:

  • CPU 各簇负载(Mach host statistics)

  • CPU/GPU 频率(IOReport residency)

  • CPU/GPU/ANE/DRAM 功耗(IOReport 能量计数器)

  • 整机功耗、温度、风扇(SMC)

  • 内存、swap、内存压力、电池

  • 进程级 CPU、内存、能耗、GPU、磁盘

台式机注意:Mac mini 和 Mac Studio 没有电池,那一栏空着,正常。

跟同类工具摆一起看(README 的对比表):

siltop macmon asitop btop
功耗 有 有 有 无
免 sudo 是 是 否 是
进程级能耗 / GPU 有 无 无 无
机器可读输出 JSON / NDJSON / Prometheus / CSV 仅 JSON — —

进程级能耗这条看着对跑模型的人最有用,实测要打折:M6 上进程级 energy_w 有值,但推理时 ollama 那个进程只显示约 0.36 W,明显只算了 CPU 侧,GPU 那部分没算进去。算模型电费还得看整机 sys_power。

作者在 M5 Pro 上、1 秒间隔测过自身开销:siltop 约占单核 1.3%,btop 约 2.5%,macmon 约 1.4%。这是作者的数字。我用 siltop doctor 自测,250ms 间隔、8 个 tick 平均 CPU 0.81%,口径不同,不能直接对着比。另外它默认不联网,更新检查默认关。

装上

两条路,选一条。Homebrew 最省事:

1
brew install cloaky233/tap/siltop

或者下预编译包,装进 ~/.local/bin:

1
2
3
mkdir -p ~/.local/bin && curl -fsSL \
https://github.com/CLoaKY233/siltop/releases/download/v1.2.1/siltop-v1.2.1-macos-arm64.tar.gz \
| tar -xz --strip-components 1 -C ~/.local/bin siltop-v1.2.1-macos-arm64/siltop

坑在这儿:预编译包没做苹果公证,浏览器下载回来的带隔离属性,第一次跑会被 Gatekeeper 拦;上面那条 curl 管道装的没这问题。习惯浏览器下载就补一句:

1
xattr -d com.apple.quarantine ~/.local/bin/siltop

装完先自检:

1
siltop doctor

mini6 上的结果:识别为 Apple M6,机型 Mac18,5,6E+4P+2S 核、12 核 GPU、32GB 内存;chip table 是 none (prefix fallback),也就是还没有 M6 专用表,按前缀兜底。collector 全部 Ok;SMC 读到 148 个温度键、59 个功耗键;437 个进程里 262 个可读、175 个需要 root。

想先看看长什么样、又不想制造负载:

1
siltop --demo    # 回放一段录好的 60 秒会话

实测:先空载,再推理

命令和数字一起给。我是用预编译包装进 ~/.local/bin 的,没动 sudo,sha256 校验通过。

第一步,先确认 JSON 结构。版本不同字段名会变,别照抄别人写的路径:

1
2
siltop --json | jq 'keys'
siltop --json | jq '.cpu'

mini6 上的顶层字段:schema_version、unix_ms、interval_ms、primed、soc、cpu、gpu、memory、network、disk、temp、fans、thermal_pressure、sys_power、all_power、cpu_power、gpu_power、ane_power、ram_power、gpu_freq_mhz、battery、processes、verdict、health。

整机功耗是顶层的 sys_power,GPU 功耗是 gpu_power,温度在 temp.cpu_temp_avg / temp.gpu_temp_avg / temp.hotspot_c。没有 power 这个嵌套对象,网上抄的示例路径在这台机器上跑不通。后面命令里的 key 都按你机器上的实际输出改。

第二步,空载基线,采 60 个 1 秒样本:

1
siltop --json-stream -i 1s --count 60 > idle.ndjson

说明一下:我没把 ollama 停掉,qwen2.5-coder:14b 常驻内存但没在算,后台还有两个语音服务。这一步测的是日常待机,不是关干净的空载——日常本来就是这个样子。

第三步,推理负载。一个终端开始生成,另一个终端同时录:

1
2
3
4
ollama run qwen3.8:27b-mlx "写一篇 800 字的说明文,解释为什么天空是蓝的"

# 终端 B,在它开始输出的同时按下
siltop --json-stream -i 1s --count 60 > run.ndjson

我用的是 ollama 的 /api/generate 接口,think 关闭,连续跑两次、各生成 800 token。

第四步,算平均瓦数。NDJSON 一行一个 JSON,用 Python 比 jq 好写:

1
2
3
4
5
import json, statistics
rows = [json.loads(l) for l in open('run.ndjson') if l.strip()]
watts = [r['sys_power'] for r in rows if r.get('sys_power') is not None]
print('样本', len(watts), '平均', round(statistics.mean(watts), 1), 'W',
'峰值', round(max(watts), 1), 'W')

想看 GPU 那一项,把 r['sys_power'] 换成 r['gpu_power']。

两组结果:

日常待机(60 个样本) 推理(GPU 占用 >50% 的 72 个样本)
整机 sys_power 平均 5.73 W(最低 2.29,最高 13.46) 平均 43.18 W,峰值 50.96 W
GPU gpu_power 0.04 W 平均 18.23 W,峰值 21.55 W
GPU 占用 / 频率 约 5% / 约 562 MHz 约 99% / 约 1650 MHz(1449–1697)
CPU 占用 — 约 8%
GPU 温度 均值 41.7°C 均值 73.1°C,最高 85.05°C
热点 hotspot_c 均值 55.6°C 最高 92.78°C
CPU 温度 均值 41.9°C 最高 73.3°C
风扇 1000 rpm 1000 rpm 升到最高 1182 rpm(上限 4900)
thermal_pressure nominal 全程 nominal,没有降频告警

速度:两次 eval 分别是 800 token / 44.56 s 和 800 token / 43.98 s,平均约 18.1 tokens/s。

整机功耗实测:待机与推理两条曲线

整机功耗实测:待机与推理两条曲线

M6 的分项读数:整机功耗、GPU 功耗、GPU 占用和频率、各温度、风扇转速都正常有数;cpu_power、ane_power、ram_power 三个分项全是 null。想看 CPU 自己几瓦、ANE 几瓦,在 M6 上暂时没戏,看整机和 GPU 没问题。

原始数据我留在 mini6 的 ~/siltop-bench/ 下(idle.ndjson、run.ndjson、gen.jsonl),以后想换算法重算不用重新跑。

每千 token 电费

公式很短,Wh 是瓦时,1 度 = 1000 Wh:

1
2
3
耗电 Wh = 平均整机瓦数 × 运行小时数
每千 token 耗电 = (平均瓦数 × 生成耗时秒 / 3600) / (生成 token 数 / 1000)
电费 = 每千 token 耗电 / 1000 × 电价(元/kWh)

把上面的实测代进去:推理整机平均 43.18 W,约 18.1 tokens/s,即每千 token 要生成约 55.3 秒。43.18 × 55.3 / 3600 ≈ 0.66 Wh/千 token;扣掉 5.73 W 的日常待机,增量约 0.58 Wh/千 token。

电价按我家实际的 0.55 元/度算(你按自家电价换):每千 token 约 0.00036 元,每百万 token 约 0.66 度、约 0.36 元。一晚上按 8 小时满载连续推理算,43.18 W × 8 h ≈ 0.35 度,约 0.19 元;同样 8 小时只待机,约 0.05 度。这台 mini6 跑一整晚的电费,两毛钱左右。

边界和坑

  • 只支持 Apple Silicon,README 写 M1 到 M5。Intel Mac 别下单。M6 我实测能跑:整机和 GPU 功耗、温度、风扇正常,CPU/ANE/内存分项功耗读不到(全是 null)。

  • 预编译包未公证,浏览器下载回来要 xattr -d com.apple.quarantine,这是最容易被劝退的一步。

  • macOS 27 上部分 IOReport 功耗计数器几秒才刷新一次。这时候 siltop 不会给你中间插值出一个假的 0 W,它会显示上一次的真实读数,并标出这个读数的年龄(大约 4s)。第一次看到会以为是 bug,其实它比你自己的猜老实。

  • 台式机没有电池读数,Mac mini/Studio 上那一栏空着。

  • siltop 是观测工具,不是散热工具,它告诉你多少瓦、多少度,降不降频还得你自己想办法。

命令清单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
brew install cloaky233/tap/siltop        # 装
siltop doctor # 自检
siltop --demo # 无负载回放
siltop # 仪表盘
siltop -i 1s # 1 秒刷新
siltop --surface glance # 极简面版
siltop line --style tmux # 单行状态,例:cpu 12% gpu 3% 8.4W 51°C
siltop --json | jq 'keys' # 先看结构
siltop --json | jq '.cpu.clusters'
siltop --json-stream --count 3 -i 500 # 3 个样本,每 500ms
siltop --json-stream -i 1s --count 60 > run.ndjson # 录 60 秒
siltop report week --json # 按应用的能耗、峰值温度
siltop record --duration 10m # 录 10 分钟
siltop replay # 回放
siltop serve # 127.0.0.1:9101 出 Prometheus 指标
siltop fleet studio mini.local # 经 ssh 并排看多台 Mac(换成你自己的主机名)

我常用的三条:line --style tmux 塞进 tmux 状态栏日常盯着;report week --json 回答”这周到底是哪个 App 把电吃了”;serve 那个端口留给以后的 Grafana。

结论

这台 mini6 满载跑一整晚,电费两毛钱左右。卡住我的从来不是问题本身,是”看功耗 = 要 root”这个默认设定——工具换了,事情就成了。