Tiana随笔

每瓦特 Token 数:算力成本的新视角

· 工程与效率

标签:#AI #部署

每瓦特 Token 数:算力成本的新视角

AI 行业的瓶颈正在从算法转向能源和芯片。“单位功耗产出”这个指标,正在影响从数据中心到笔记本的一切设计。

最近帮一个朋友算一笔账:他想在本地用一张消费级显卡长期跑一个 7B 的小模型,给自己的内部工具做摘要和分类。他去比价云 GPU 实例,又去查显卡功耗,最后卡在一个问题上一筹莫展,"我到底该怎么判断,这个方案划不划算?"我看了眼他列的两张表:一张是各家云的每小时单价,一张是显卡的 TDP 标称功耗。这两张表用的是两套语言,根本对不上。

这恰恰暴露了大多数算力成本讨论的盲区:我们习惯用"每小时多少钱"或者"每张卡多少推理速度"去衡量,却很少有人把"电费、散热、部署密度"放进同一把尺子。当我建议他用"每瓦特能产出多少 Token"作为统一度量时,那两张割裂的表忽然有了交集。这篇文章想聊的,就是为什么"tokens per watt(每瓦特 Token 数)"是一个被严重低估的视角,以及作为个人开发者,怎么用很低的成本给自己做个能效粗测。

下面的示意图把"花钱,耗电,出 Token"三件事串成了一条链,方便后面展开。

为什么需要一把统一的尺子

先看问题出在哪。云 GPU 的账单告诉你的是"钱",通常是按卡时或按 Token 计费;硬件规格告诉你的是"功率",通常是 TDP 这种峰值热设计功耗;而模型告诉你的是"产出",也就是 tokens/s。这三者在单位上完全不互通:你没法直接拿 美元/小时 去和 瓦特 比较,也就没法回答"我把模型量化到 4bit 后,到底是更省钱了还是只是更慢了"。

"每瓦特 Token 数"的妙处在于,它是一个把三者缝合起来的派生指标:分子是产出(Token),分母是能耗(瓦特,可进一步折算成电量和电费)。当你用这个指标去看方案,很多原本模糊的权衡就清晰了。

能效 = 输出 Token 数 / 消耗功率(瓦特)
单位:tokens / (W·s) 或直接看 tokens 每瓦时 (tokens/Wh)

举一个直觉化的例子:方案 A 用一张 300W 的卡跑出 60 tokens/s,方案 B 用一张 75W 的卡跑出 20 tokens/s。单看速度 A 完胜,但算能效:A 是 0.2 tokens/(W·s),B 是 0.27 tokens/(W·s):B 反而更"省电"。如果你的负载是 7×24 常驻、对延迟不敏感,B 可能长期更便宜,因为它电费低、发热小、不需要额外散热投入。

我越来越倾向把能效当成"第二成本"。租金是显性的、按月扣的;电费和散热是隐性的,但它决定了你能把多少算力塞进同一个机柜、同一个插座、同一张电费单。

定义与测量的基本思路

先说清"瓦特"测的是什么。严格意义上,你应该测的是"整机的推理功率",而不只是显卡 TDP。显卡标称 75W,实际供电转换、CPU、内存、主板都会吃电。个人项目精度要求不高,但概念要对:你真正关心的是"从插座端量到的功率",因为它才是电表上跳的数字。

测量思路分三步:

第一步,固定负载。让模型在一段固定时间内持续生成(比如用一段长 prompt 让它连续输出 2000 个 Token),期间不要让别的任务抢资源。负载不稳,测出来的数没有意义。

第二步,测功率。最朴素的是用智能插座(带功率计量那种)读整机瓦数;进阶一点用显卡自身的功耗读取接口(如 nvidia-smi 的 power.draw)。记录这段负载期间的平均功率 P(瓦特)和持续时间 t(秒)。

第三步,算产出。记下这段时间模型实际吐出了多少 Token,记为 N。那么:

平均功率 P (W)
持续时间 t (s)
输出 Token 数 N

tokens per watt-second = N / (P × t)
tokens per watt-hour   = N / (P × t / 3600) = 3600 × N / (P × t)

我用"每瓦时 Token 数"更直观,因为电费也是按千瓦时(度)算的。假设你那边电价是 c 元/度(1 度 = 1 kWh = 1000 Wh),那么每千 Token 的电费成本大约是:

每千 Token 电费 = 1000 / (tokens per Wh) × (c / 1000) 元
               = c / (tokens per Wh) 元

这样你就能把"瓦特"换算回"钱",和朋友那张云价目表终于说上话了。

影响能效的四个因素

能效不是固定的硬件常数,它会被好几股力量拉扯。理解这些,你才能在自己的约束下做取舍。

量化精度

把模型从 FP16 压到 INT8、INT4,显存占用和带宽压力都降下来,通常能在"略微掉点精度"的代价下换来明显更高的 tokens/s 和更低的峰值功率。但注意:量化不是免费的午餐。太激进的量化(比如 2bit)可能让模型胡说八道,等于"产出"这项分子被污染了,你产出的 Token 很多,但能用的没几个,真实能效反而下降。我的经验是 4bit 到 8bit 之间对个人项目通常是甜区。

批处理大小

这是最反直觉的一点。很多人以为"一次只处理一个请求最省电",其实不然。现代推理框架在 batch 模式下能把 GPU 的并行算力吃满,单位 Token 的能耗反而更低。一次处理 16 个请求,可能比逐个处理 16 次的总能耗低一大截。代价是延迟上升和显存占用增加。如果你做的是离线批处理(比如夜里给一堆文档做摘要),把 batch 调大几乎是纯赚能效。

硬件代际

新架构往往在"相同功耗下跑更多算力"。比如同功耗的 newer generation 显卡,由于更高效的张量核心和更高的内存带宽,tokens per watt 可能比老卡高不少。所以"老卡虽然便宜但费电"在长时间运行场景下可能反被追上,这也是为什么前面说要把电费算进总账。

内存带宽墙

这一点很多人忽略。大模型推理很多时候不是被算力卡住,而是被"把权重从显存搬到计算单元"的内存带宽卡住。一个模型哪怕算力再强,如果显存带宽不够,实际 tokens/s 上不去,能效自然也上不去。这解释了为什么同样参数量,放在高带宽显存(如 HBM)的卡上,能效显著高于放在普通 GDDR 的卡上。买卡或选型时,别只看 TFLOPS,要看带宽。

能效是一张多变量的网,没有单一开关。我的建议是:先固定量化精度和批大小这两个你最能控制的旋钮,再去谈硬件。

自托管小模型做能效粗测

说点落地的。如果你手头有一张卡和一个能跑模型的框架(以 llama.cpp 或 vLLM 为例),粗测能效并不复杂。

先用框架自带的统计拿到 tokens/s:

# llama.cpp 常见做法:固定 prompt,连续生成,看日志里的 eval rate
./llama-cli -m model-q4.gguf -p "请连续写一段关于秋天的散文" -n 2000 --no-warmup
# 日志会打印类似:eval time = 30.00 s, eval rate = 66.67 tokens/s

同时用 nvidia-smi 采样功率(如果是有独显且驱动支持):

# 每 100ms 采样一次功耗,持续 35 秒
nvidia-smi --query-gpu=power.draw --format=csv,noscale -l 0.1 -c 350 > power.log

然后做个简单折算。假设 eval rate 是 66 tokens/s,平均 power.draw 是 140W,跑了 30 秒:

tokens_per_s = 66.67
avg_power_w = 140.0
duration_s = 30.0

total_tokens = tokens_per_s * duration_s
energy_wh = avg_power_w * duration_s / 3600.0
tpw = total_tokens / energy_wh  # tokens per watt-hour

print(f"本次产出 {total_tokens:.0f} tokens,耗电 {energy_wh:.3f} Wh")
print(f"能效 ≈ {tpw:.1f} tokens/Wh")

跑出一个数之后,换量化(q4 改 q8)、换 batch(1 改 16)、换模型,横向对比,你就能画出自己那张"能效曲线"。这比看任何跑分文章都准,因为跑分是在别人的硬件、别人的负载下测的,而你关心的是你自己的场景。

没有独显功耗接口也没关系,插个带计量的智能插座读整机瓦数,误差大一点但趋势绝对够用。我第一次测就是用插座,发现待机功率比我想象高,原来机器上还有一堆常驻服务在吃电,于是我把推理机和日常开发机分开了。

云账单与瓦特的换算视角

云厂商通常不会告诉你"每瓦特多少钱",他们只给你"每卡时"或"每百万 Token"。但你可以反推。假设某云 GPU 实例 2 元/小时,显卡整卡功耗约 300W:

每小时电费(按 0.8 元/度估算,仅示意)= 0.3 kW × 0.8 元 = 0.24 元
实例租金 2 元中,电费占比约 12%,其余是折旧、运维、利润

这个拆解的意义在于:当你看到"云上跑比自建贵 5 倍"时,要意识到那 5 倍里有一大块是别人替你付的电费、散热、机位和运维,打包进了租金。自建便宜的前提是你自己的电费和散热成本真的低,且设备利用率够高。如果你一张卡一年只跑几百小时,折旧摊下来比租金贵得多,这时候"别人代付电费"反而是更经济的选择。

真正的权衡不是"云贵还是自建贵",而是"我的负载密度,能不能把自建的固定成本摊薄到低于云的边际成本"。密度越高,瓦特这把尺子越偏向自建。

冷静看待环保叙事

聊能效不提环保几乎不可能,但我更愿意冷一点看。很多人直觉里把"用更少电"直接等同于"更环保",这个等式在个体层面成立,在系统层面却要打问号。

原因在于规模效应与使用强度的关系。一个能效更高的模型,可能让更多人更低门槛地大规模调用 AI,总耗电量反而因为"需求被激发"而上升,这就是 rebound effect(回弹效应)。单独看每次推理更绿了,看总量未必。另一方面,如果你把闲置的高功耗机器换成按需启停、能效更高的小模型,确实能在自己这一亩三分地里实打实省电,这个微观改善是真实的,不该被"宏观上可能抵消"的论调否定掉。

我的态度是:把能效当成"省钱和可持续的副产品"去追求,而不是当成道德勋章。每瓦特 Token 数涨上去,你的电费单和碳排都在下降,这是确定可得的好处;至于它能不能拯救地球,那是产业级的问题,不是你一个个人项目该背的包袱。

电源效率与待机功耗:两个隐形项

前面折算时我默认"插座读到的瓦数就是真实能耗",但有两处隐形损耗值得拆开看,否则你的能效账会偏乐观。

第一是电源效率。整机从插座取的电,经过电源(PSU)转换后才供给主板和显卡,转换过程有损耗。一个 80 Plus 金牌电源在典型负载下约 90% 效率,意味着你读到的 300W 输入,真正用到计算上的只有 270W 左右。更老或更便宜的电源可能只有 80% 甚至更低。做精确能效时,分母用"插座输入功率"没问题(因为电费按输入算),但如果你要对比"芯片本身的能效",就得意识到中间那 10%–20% 被电源吃掉了。

第二是待机功耗,这个对个人项目杀伤力最大。很多人算账时只算"推理时的功率",却忽略机器 7×24 开机、但真正在跑模型的时间可能只占 10%。剩下的 90% 里,CPU、主板、风扇、硬盘都在吃电,可能也有 50W–100W 的待机消耗。当你把这部分摊进"每瓦特 Token 数",真实能效会被显著稀释。这也是为什么我强烈建议:如果负载不是持续高压,就让推理机支持按时启停(比如通过脚本在收到请求时唤醒、空闲几分钟自动挂起),或者干脆把低频任务丢给云上的按需实例,把"待机电费"转嫁给按量计费模型。

一句话:看能效不能只看峰值,要看"负载因子"。同样一张卡,每天跑 20 小时和每天跑 1 小时,摊到每个 Token 上的电成本能差出好几倍。

测量中的误差与陷阱

粗测归粗测,但有些坑会让你的数彻底失真,列出来避坑。

采样间隔过粗会漏掉尖峰。GPU 推理在生成开始时往往有个瞬时功率冲高(权重加载、kernel 预热),如果你用 1 秒一次采样、又只跑了 3 秒,可能正好错过或夸大这个尖峰。建议采样间隔压到 100ms 以内,且负载持续时间足够长(30 秒以上),让瞬时波动被平均掉。

warmup 必须排除。第一次生成通常包含编译、缓存建立等一次性开销,tokens/s 会偏低、功率曲线会畸变。正确做法是先空跑一轮 warmup,再正式计时测速,否则你会系统性低估能效。

burst 负载要小心均值。如果你测的是"请求来了才跑、跑完就歇"的模式,平均功率会很低(因为大量时间是 0 负载),但这时候分母为小、分子也为小,算出来的每瓦时 Token 数会虚高,它反映的不是稳态推理能效,而是"大部分时间在睡觉"。这种数可以用来算电费,但不能用来横向对比不同硬件的推理效率。

最后,显卡 power.draw 和整机插座读数之间会有 GAP(CPU、内存、主板)。用 nvidia-smi 测出来的能效天然比插座测的好看,跨方案对比时务必统一用同一种测量口径,否则就是在拿苹果比橘子。

把能效变成采购决策

把前面的碎片拼起来,你能回答朋友最初的问题,"自建还是上云"。我给自己定的决策树大致是:

  • 如果月负载低于几百卡时,且无法稳定填满显卡:优先云按需,别让自建的待机电费和折旧反噬。
  • 如果负载持续高位、对延迟不敏感:自建高 batch 的小模型,用能效尺子选"每瓦时 Token 数"最高的组合,长期电费优势明显。
  • 选型时把"带宽墙"和"代际"一起看,不要只比价格和 TFLOPS。

这把尺子不保证你每次都选对,但它让"凭感觉"变成"有据可循"。每瓦特 Token 数涨上去的那一刻,你省下的不只是电费单,还有机箱里那股该死的热风。

把决策树的几个分支写成函数,朋友那个问题就可以直接算了:

def build_or_cloud(gpu_hours, watts, kwh_price, cloud_per_hour):
    """自建每月电费 对比 云端每月账单"""
    if gpu_hours < 300:
        return 0, round(gpu_hours * cloud_per_hour, 1), "上云,卡填不满"
    self_cost = gpu_hours * watts / 1000 * kwh_price
    cloud_cost = gpu_hours * cloud_per_hour
    verdict = "自建" if self_cost < cloud_cost * 0.6 else "上云"
    return round(self_cost, 1), round(cloud_cost, 1), verdict


print(build_or_cloud(40, 300, 0.6, 1.8))
print(build_or_cloud(600, 300, 0.6, 1.8))
print(build_or_cloud(600, 300, 1.5, 1.8))

Token 该怎么数,以及训练与推理是两笔账

在埋头测能效之前,有两个概念陷阱会让你算出来的数"看起来可比、其实不可比",必须先拆清。

第一个陷阱是 Token 的计数口径。中文场景下尤其明显:同一个句子,用 Llama 的分词器可能切成 12 个 token,用 Qwen 的可能切成 9 个,用 GPT 系列又不一样。于是"tokens per watt"这个分子,本身就依赖于分词器。跨模型家族比 tokens/s 或 tokens/Wh,常常是在比橘子对苹果,你以为 B 比 A 能效高 20%,可能只是 B 的分词器更"省 token",实际吐出的语义量未必更多。我的应对是:要么在同一分词器下比较(都跑同一个模型的量化变体),要么干脆不数比特级的 Token,改比"完成同一个真实任务(比如摘要 100 篇文章)的总能耗",让任务而不是 token 当统一单位。

第二个陷阱是混淆训练能耗与推理能耗。前面全文聊的都是推理(inference)能效,因为它是个人项目日常在付的电费。但如果你打算自己微调模型,训练那一次性(也可能持续多天)的能耗往往比推理累计还高几个数量级,一次全参数微调 7B 模型可能耗电几十度。所以决策时要分清:你是为"持续服务的电费"优化,还是为"偶尔一次训练的电费"优化?两者策略不同,后者通常更该交给云上弹性资源,跑完即弃,不占用你本地常驻电力。

再补一层"边缘设备的账"。前面默认你在用独显,但如果你的负载很轻,完全可以用 CPU 甚至树莓派这类边缘设备跑小模型。这时候能效的衡量要换尺子:没有高带宽显存,纯靠内存,tokens/s 低,但整机功率可能只有 10W 出头。对于"每天几十次请求"的个人工具,这种方案的每瓦时 Token 数未必漂亮,可它零额外散热、插电就用、静音,综合拥有成本反而最低。所以没有"最高能效"的通用答案,只有"在你的负载和部署形态下最合算"的答案。

衡量之前先问自己:我比的是同一个分词器下的 token 吗?我比的是推理还是训练?我部署在独显还是边缘?把这三个问题答了,你的能效数字才立得住。

一个完整的自测账本示例

前面讲了那么多方法,最后用一笔具体的账把流程串起来,方便你照抄。假设我手头有一张 70W 的闲置显卡,想判断"常驻跑一个 7B 量化模型做内部摘要"到底划不划算,对比对象是某云 2 元/小时的同级别实例。

我先用 llama.cpp 在固定 prompt 下连续生成 3000 个 Token,同时用插座读到整机平均功率 95W(含 CPU 和主板),耗时 45 秒。折算:

tokens = 3000
power_w = 95.0
seconds = 45.0

energy_kwh = power_w * seconds / 3600.0 / 1000.0
tpkwh = tokens / (energy_kwh * 1000.0)  # 每千瓦时 Token 数
price_per_kwh = 0.8
cost_per_1k = (1000.0 / tpkwh) * (price_per_kwh / 1000.0)

print(f"能效 {tpkwh:.0f} tokens/kWh,每千 Token 电费约 {cost_per_1k:.4f} 元")

跑出来大约是 250 万 tokens/kWh 的量级,每千 Token 电费不到 0.0004 元。哪怕我一天跑 50 万 Token,电费也才两毛钱。这时再算云:2 元/小时,假设云端同样负载能跑出 80 tokens/s,45 秒约 3600 Token,每小时电费等价约 2 元,折合每千 Token 约 0.55 元,比自建贵了上千倍。

但别急着下结论。这个对比成立的前提是"那张 70W 显卡本来就闲置、且 7×24 开机不增加额外待机成本"。如果为了它我得新买机器、新增散热、且每天只跑十分钟,待机功耗摊进去后自建优势立刻缩水。所以账本里一定要写清楚假设:负载因子多少、设备是否闲置、电价多少。把假设摆上台面,结论才有意义,否则就是拿最理想的自建场景去碰最贵的云场景,赢了也不体面。

我一直觉得,能效账本最大的价值不是得出"谁更便宜"这个结论,而是强迫你把隐藏假设(待机、负载、电价、折旧)一条条写下来。写下来的瞬间,很多"我觉得云太贵"的直觉,会被自己列出的数字修正。

能效不是唯一坐标:延迟与体验的让渡

写到这里必须诚实地补一刀:追求每瓦特 Token 数,往往会让你付出另一项成本,延迟。前面说把 batch 调大能抬高能效,可 batch 越大,单个请求要等前面排队的请求处理完,首字延迟(TTFT)就越高。对于离线批处理这无所谓,但对于"用户对着输入框等回复"的在线场景,延迟直接等于体验。所以真实决策是一个天平:你愿意用多少延迟,换多少电费?

我的取舍经验是分场景定策略,而不是全局最优。在线交互用小 batch、甚至 batch=1,保住响应速度,为此接受稍低的能效;深夜的离线任务(批量摘要、日志归类)切到大 batch,把能效拉满,反正没人等。同一张卡、同一套代码,靠调度策略在不同时间段扮演两个角色,比单纯"为了省电牺牲体验"聪明得多。

另一个常被忽略的维度是电网本身的碳强度。每瓦特 Token 数衡量的是"电的效率",但没衡量"这度电干净不干净"。同样跑出 100 万 Token,在煤电为主的电网里碳排放,远高于在水电、风电为主的电网。所以如果你真在意环保叙事,除了提高能效,还可以考虑把重负载调度到电网碳强度低的时段或地区(云厂商某些区域绿电比例更高)。这已经超出了纯个人项目的范畴,但作为"冷静看待环保"的延伸,值得记一笔:能效优化降低的是电耗,碳排还取决于电从哪来。

最后提醒,别被单一指标绑架。我曾见过有人为了把 tokens/Wh 再提升 5%,花两周折腾内核参数和电源方案,结果省下的电费远不够抵偿人工。能效是工具,不是目的;当优化它的边际收益低于你的时间成本,就该停手了。

把能效、延迟、碳强度、人工成本放在同一张决策桌上,你才会发现:最好的方案,往往是综合账最舒服的那个。

一次量化对比的小实验记录

光给框架不够直观,补一段我自己跑过的对比(数字为当时实测量级,仅作相对参考,不宣称精确跑分)。同一张卡、同一个 7B 模型,我分别用 q4 和 q8 两种量化跑了前面那套固定 prompt 测试。q4 时显存占用约 4.5G,平均功率 92W,tokens/s 约 66;q8 时显存约 8G,平均功率 98W,tokens/s 约 58。

单看速度,q4 反而更快,这有点反直觉,但原因就在前面说的"内存带宽墙":q8 权重更重,每次要把更多字节从显存搬进计算单元,带宽成了瓶颈,算力没用完,速度反而被拖慢;同时功率略高。算能效,q4 大约多出 15% 的每瓦时 Token 数。表面看 q4 完胜,可当我把两个模型对同一批摘要任务做质量抽检,q8 在长文本连贯性和数字准确性上明显更稳,q4 偶尔会漏掉关键句。

这就回到了开头的取舍:能效高的方案,如果产出的 Token 里有相当比例不可用,真实"有效能效"要打折扣。我最后的选择是,对质量要求高的摘要任务用 q8(接受稍低能效换准确率),对"粗筛哪些文档值得细看"这类容错任务用 q4(能效优先)。同一硬件上按任务分档,比全局追一个最高能效数字更务实。这也说明,你测出来的每瓦特 Token 数必须配合"产出质量"一起读,孤立的能效数字会骗人。

实验的价值从来不是证明"q4 比 q8 好",而是让我亲眼看见:能效、速度、质量三者如何互相拉扯,以及我的具体负载里哪头更该让步。别人的结论替不了你自己的那张账本。

相关文章