大模型优化与能效评估:功耗采样、量化与tokens/J实践 第一次做模型量化的时候我以为把权重从 16 位压到 4 位功耗就该掉到四分之一。把功耗采样挂上去连跑了十几个小时整机功耗曲线只往下挪了不到两成单 token 能耗tokens/J的改善更只有一成出头。那次测量把我对绿色计算的不少想当然敲碎了能效这事儿不看你省了多少参数、压了多少比特只看一段时间里到底消耗了多少焦耳而中间隔着内存带宽、电源转换效率、散热能力和调度策略一整套链路。这篇内容聊的就是大模型优化与能效评估这条线怎么定义指标、怎么测、从哪几个层面动手优化、哪些数字看着漂亮其实是假的。适合正在被电费账单或算力预算追着跑的同学也适合要给团队做技术选型、需要拿出一份站得住脚的能效报告的人。哪怕你只是想知道自己那张卡到底在替谁烧电往下看也不会白费时间。1. 把“绿色”拆成两本账电费口径与碳排口径1.1 能效为什么突然变成绕不过去的指标大模型落地之后成本的构成变了。早期做算法的人习惯盯准确率工程侧习惯盯延迟和吞吐这两组指标都还在但它们的货币化分母不一致准确率决定业务能不能用延迟决定用户体验而能耗直接决定了这门生意在规模化之后还剩多少毛利。一次推理请求的算力成本在推理量级从每天几万涨到每天几千万之后量变会引发质变——单位成本上的任何一点浪费都会被乘以一个很大的系数。我见过不少团队在优化时只盯吞吐每秒能出多少 token但吞吐高不等于能效高。把批大小拉大吞吐上去了可单卡功耗也跟着爬到功耗墙附近最后算下来 tokens/J 只提升了个位数百分比。反过来一些看起来“慢”的配置因为避开了降频、减少了无效计算单位 token 的能耗反而更低。这就是为什么需要把能效当成一个独立指标来优化而不是把它当成吞吐的附赠品。从更长的周期看能效还绑着另一件事硬件折旧。满载高温运行的卡寿命和稳定性都会打折故障率上升带来的隐性成本往往比电费本身还高。所以我说“绿色计算”在工程语境里不是情怀它是把资源利用率、成本、稳定性三件事捆在一起的一种视角。1.2 训练和推理的能耗结构完全不是一回事新手最容易犯的错是把训练侧的经验直接搬到推理侧。这两者的能耗结构差异非常大优化手段的优先级也因此完全不同。训练侧的能耗特征是长时间、高功率、接近满载。一个训练任务的功耗曲线通常很平起跑阶段爬升之后长期贴着功耗上限跑偶尔因为数据加载或检查点写入掉一点。它的能耗大头在算力本身所以优化方向是提高每一焦耳对应的有效浮点运算量混合精度、减少重计算、减少通信等待、提高并行效率。推理侧的能耗特征是波动大、空闲占比高、受显存带宽限制。线上服务的功耗曲线是锯齿状的请求密集时冲高请求稀疏时掉到待机功耗。很多团队忽略了待机功耗这一块——一张卡空转也会持续消耗几十瓦到上百瓦一个几十卡的推理集群空载时段累计的电量相当可观。推理侧的能耗大头往往不在算力而在数据搬运权重从显存搬到计算单元的带宽开销、KV cache 的读写、以及为长尾请求保留的显存冗余。下表是我自己在做评估时常用的一个对照帮助判断该往哪个方向使劲维度训练场景推理场景功耗曲线形态长平台期、接近满载锯齿状、空闲占比高主要瓶颈算力与通信效率显存带宽与调度效率能效优化杠杆混合精度、重计算取舍、并行策略量化、批处理、缓存复用、长度控制单次测量窗口数小时到数天数分钟级稳态需覆盖高低峰常见误判用小规模短跑外推长时间训练只测高峰不测空闲1.3 卡级、整机级、机房级三个口径不能混着说能效数字最容易出问题的地方不是测量精度而是口径混乱。同一套优化在卡级口径下可能提升 30%在整机级口径下只剩 15%到机房级可能不到 10%。原因很直白卡级只算加速卡本身的功耗整机级要加上 CPU、内存、风扇、网卡和电源转换损耗机房级还要加上制冷、配电损耗也就是常说的 PUE 系数。我在内部做对比时坚持三件事第一任何能效数字必须带口径标签写清楚是卡级还是整机级第二跨机器、跨机房的对比必须先统一到同一口径否则没有意义第三涉及对外的口径一律取最保守的那一档宁可把收益说小一点也不要因为口径被质疑而推翻整份报告。一个具体的换算提醒电源的转换效率不是 100%。常见的高效率电源在典型负载下转换效率在九成出头低负载时效率还会往下掉。这意味着你在卡级看到的 100 瓦节余到墙面插座上可能是 110 瓦左右的实际节余按效率折算而机房级还要再乘 PUE。做预算的时候如果按卡级数字直接乘电价结果会系统性偏低。2. 能效评估的测量链路采样、积分与边界2.1 采样频率决定了你能不能看到真相功耗不是读一次就能拿到的东西它是一个随时间变化的量你要的能量是它对时间的积分。采样间隔太粗锯齿状的负载变化会被平均掉算出来的能量和实际情况偏差可能超过 20%。采样间隔太细驱动查询本身的开销会污染被测程序尤其是高频查询时查询线程可能抢占计算资源。我的经验区间是100 毫秒到 1 秒。推理服务的请求波动通常在这个尺度以上100 毫秒能捕捉到大部分峰谷训练任务更平缓1 秒足够。如果做的是短请求高并发场景可以把采样压到 50 毫秒但要单独跑一次空载对照确认查询本身带来的额外功耗可以忽略。还有个细节采样时刻的抖动。如果你的采样循环用固定间隔睡眠实际间隔会被系统调度拉长误差会累积。正确做法是记录每次采样的真实时间戳用时间戳做积分而不是用名义间隔乘以次数。这个坑我在第一次做长时间测量时踩得很实在跑了两小时的测试实际采样次数比预期少了 7%积分出来的能量自然就偏了。2.2 三个最容易搞错的口径第一个是空载功耗是否计入。做 A/B 对比时如果 A 方案测的是满载窗口B 方案测的是包含预热和冷却的完整窗口两者根本不可比。我的做法是统一定义“有效测量窗口”负载进入稳态后开始计时稳态结束立即停止预热和收尾单独记录、单独报告。第二个是峰值功耗和平均功耗的混淆。峰值功耗决定你的供电和散热设计平均功耗决定电费。有人拿峰值功耗乘时间来算电费结果高估一倍以上也有人拿平均功耗去评估散热余量结果机器一到夏天就降频。第三个是是否包含 CPU 和内存。大模型推理在批大小小、序列短的时候CPU 侧的前后处理分词、调度、结果拼装占比不低有些场景下 CPU 侧的能耗能占到整机的两成。只测加速卡会系统性低估优化收益尤其是你优化的是调度逻辑的时候——优化完卡侧功耗没变但 CPU 侧降了一半这个收益只有整机口径才看得见。2.3 一段可以直接抄的采样积分脚本下面这段是我常用的最小骨架思路是固定频率采样、记录真实时间戳、梯形法积分、输出能量和能效指标。具体查询接口按你手上驱动的文档替换逻辑不用改。import time import json def read_power(): # 返回字典加速卡、CPU、整机的瞬时功率瓦 # 具体接口按驱动或带外管理口的文档替换 return { accel_w: query_accel_power(), cpu_w: query_cpu_power(), sys_w: query_system_power(), } def run_with_sampling(duration_s, interval_s0.5, warmup_s60): samples [] t0 time.monotonic() while True: now time.monotonic() - t0 if now duration_s: break p read_power() samples.append({ t: now, phase: warmup if now warmup_s else steady, **p, }) time.sleep(interval_s) return samples def integrate_energy(samples, key): 梯形法积分返回焦耳。只用 steady 阶段。 pts [(s[t], s[key]) for s in samples if s[phase] steady] energy 0.0 for i in range(1, len(pts)): dt pts[i][0] - pts[i - 1][0] energy (pts[i][1] pts[i - 1][1]) / 2.0 * dt return energy def report(samples, total_tokens, keysys_w, pue1.2, carbon_g_per_kwh500): joules integrate_energy(samples, key) kwh joules / 3.6e6 result { energy_j: round(joules, 1), energy_kwh_facility: round(kwh * pue, 4), tokens_per_joule: round(total_tokens / joules, 4) if joules else None, carbon_g: round(kwh * pue * carbon_g_per_kwh, 1), } return result几点使用说明。积分必须用真实时间戳别用名义间隔warmup阶段的数据要保留但不要混进能量统计它可以用来判断是否真的进入了稳态比如连续几十个采样点的功率标准差低于阈值。pue和碳强度都要作为显式参数传进来避免有人默认用某个“看起来合理”的值然后忘了改。2.4 碳强度数据的取用方式与口径声明电费好算电价乘度数就行碳排就麻烦一些因为同样的电量在不同电源结构下对应的排放强度差异很大而且一天之内还会波动——白天光伏出力高的时候碳强度低夜间往往偏高。我一般建议团队做两套数一套用年度平均碳强度用于年度报告和对外口径稳定、可比另一套用逐时碳强度用于调度优化让可延迟的任务尽量落在低碳时段。两套数的用途完全不同混用会让结论变得无法解释。比如你用一个低碳时段的实测数据去证明整体方案更环保这在方法上站不住脚。还有一条边界必须写清楚只统计运行阶段还是包含硬件制造与报废。硬件制造阶段的隐含碳排在大模型场景里占比不低尤其是加速卡更新换代快的时候。如果报告里只写运行阶段一定要注明“不含硬件隐含碳排”否则读者会默认为全生命周期。这不是文字游戏口径写不清楚数据再准也没人敢用。3. 推理侧优化让每一次前向传播都值得3.1 量化能效收益远小于参数压缩比回到开头那个反直觉的结论。把权重从 16 位压到 4 位参数量占用的比特数少了 75%但功耗只降了一到两成原因是功耗的主要组成部分不是权重存储而是计算和数据搬运而低比特计算在硬件上的加速比通常小于位宽比。另外低比特推理往往需要额外的反量化步骤这部分开销会吃掉一部分收益。真正决定量化能效收益的是场景是不是显存带宽受限。在小批大小、短序列的场景下推理瓶颈在带宽权重量化带来的读取量下降能直接换成能效提升这时候收益比较明显。在批大小很大、计算密集的场景下瓶颈转向算力量化对能效的帮助就有限甚至因为反量化开销而变差。实测建议别只看权重位宽要分三个维度做对照——精度损失、延迟变化、能耗变化。我习惯画一张三列对照表任何一列恶化超过阈值就停下来重新评估。特别是精度量化在小模型上的掉点往往比大模型严重一个在中等规模上掉 0.5 个点的配置搬到小模型上掉两三个点很常见。3.2 连续批处理与 KV cache显存换能效的经典交易推理服务的能效大头往往浪费在“等”上。传统批处理要等一个批次凑齐再一起算批次里最短的请求算完了还得陪着最长的请求一起等这段等待时间里算力在空转功耗却没少多少。连续批处理把请求按到达顺序动态拼接一个请求生成结束立刻让出新位置硬件利用率能拉高不少直接体现为 tokens/J 的提升。代价是 KV cache 的显存管理变复杂。KV cache 的大小随并发数和序列长度线性增长长上下文场景下它会成为显存的主要占用者。分页式的显存管理能把碎片利用率提上去让同样的显存装下更多并发但页表查找本身有开销短序列高并发时这个开销占比不低。我的经验配比是先确认瓶颈在并发利用率还是在单请求延迟上。如果服务整体吞吐上不去而单请求延迟尚可优先做连续批处理如果单请求延迟已经是瓶颈先去压 KV cache 的精度和长度别急着加并发。两个方向同时上往往会互相抵消测出来的能效提升看着很平庸其实是两股力量在打架。3.3 投机解码与级联用便宜的计算筛掉大部分请求投机解码的思路是用一个小模型快速起草若干 token再让大模型一次性验证。验证阶段接受多个 token 就等于省下了多次大模型前向。能效收益完全取决于接受率接受率高的时候大模型前向次数成比例下降能效提升明显接受率低的时候草稿模型的开销白白付出总能耗反而上升。接受率跟任务类型强相关。格式固定、模板化的输出比如结构化抽取、分类标签生成接受率通常较高开放式创作、需要长程推理的任务接受率低。所以这套方案不能无脑上要先在你的真实流量上采一批样本测接受率低于某个阈值就别折腾了。级联是另一种更朴素的思路小模型先答置信度低或者命中特定条件再升级到大模型。它的能效优势来自“大部分请求根本不需要大模型”实现在工程上比投机解码简单也不需要改解码逻辑。难点在于置信度怎么估——用输出概率、用一致性采样、还是用一个小判别器各有各的坑。我倾向于先用一致性采样做离线评估确认分流比例合理再上线别一上来就搞复杂的判别模型。3.4 长度控制最被低估的能效杠杆输出长度对能耗的影响是超线性的因为自回归生成每一步都要读一遍 KV cache序列越长单步的显存读取量越大。把平均输出长度从 800 token 压到 400 token能耗降幅往往超过一半这个杠杆比大部分模型侧的优化都猛而且几乎不损失效果——只要你在提示词和输出约束上做对。具体做法给输出设硬上限、在系统提示里明确要求简洁、对结构化输出用 schema 约束而不是让模型自由发挥、对重复性内容做模板填充而不是让模型生成。这些手段听起来不“高级”但在真实流量上带来的能效收益经常超过一次量化实验。我见过一个团队花两个月调量化最后发现把输出格式从自然语言改成 JSON 字段直出能耗降得更多。4. 训练侧优化吞吐、显存、能耗的三方博弈4.1 混合精度与激活重计算的真实代价混合精度几乎是训练侧的标配但它对能耗的影响不是单调的。低精度计算单元吞吐高单位计算能耗低这部分是净收益。但同时低精度训练需要维护一份高精度的主权重还要做损失缩放这些额外的内存访问会带来开销。总体算下来收益是正的但没有宣传里那么夸张。激活重计算是另一个典型用算力换显存。它的能效账要看你的瓶颈在哪。如果显存不够导致批大小上不去、卡长期处于低利用率重计算能让批大小翻倍整体能效提升明显。如果显存本来就够重计算只是白白多做一遍前向能效会下降一成到三成。所以“要不要开重计算”这个问题没有标准答案取决于你的显存余量和批大小目标。我一般的判断顺序是先看显存占用曲线如果峰值贴近上限且批大小被卡住开重计算如果显存还有两成以上余量优先调并行策略或者减少中间张量的驻留别急着开重计算。4.2 并行切分与通信能耗数据并行、张量并行、流水并行、专家并行每种切分方式带来的通信量和通信模式都不一样而通信本身是耗电的——网卡、交换机、光模块都在吃电而且这部分能耗在卡级口径里完全看不到只有整机或机房口径才显现。张量并行的通信最密集每层都要做全归约跨机部署时通信能耗占比可能相当可观。流水并行通信量小但有气泡气泡期间算力空转、功耗照付。专家并行的通信是突发式的路由不均时会出现热点局部链路的功耗和温度都会上去。实操上我建议做一次“通信占比”的量化记录训练过程中通信等待时间占总时间的比例以及这段时间内的整机功耗。如果通信等待超过两成说明你的并行策略在这个集群上不划算换成更少的切分、更大的单卡批大小往往能效更好。这个结论在小规模集群上不明显到几十卡以上就非常显著。4.3 批大小与学习率联调中的能效拐点增大批大小是提升吞吐最直接的手段但能效曲线不是单调上升的。批大小小的时候硬件利用率低单位样本能耗高批大小增大利用率上升单位能耗下降但到了某个点之后功耗接近上限、频率开始下调单位能耗下降变缓甚至反弹。这个拐点就是你的能效最优点。找拐点的办法很土但有效固定其他条件把批大小按 1、2、4、8 的倍数往上扫每个点跑够稳态时间记录 tokens/J。画出曲线拐点通常在硬件利用率八成到九成之间的位置。注意这个拐点会随序列长度变化长序列场景下拐点对应的批大小会小很多因为显存和带宽压力更大。批大小变了学习率也要跟着调否则收敛变差训练步数增加能耗反而上升。线性缩放和平方根缩放各有适用条件我的经验是先用小规模实验验证收敛再上大规模别在大集群上直接试学习率——那是最贵的试错方式。5. 搭一个能跑通的小型能效测试台5.1 最小可行配置不需要一整个机房才能做能效评估。我的建议是先搭一个最小可行的测试台把方法论跑通再往大集群上套。配置上一台带一到两张加速卡的机器一个能读到整机功率的带外管理口或者一个外接的功率计接在墙插和电源之间一套稳定的推理或训练框架一份固定的测试数据集。功率计这一项别省带外管理口读到的功率通常不含电源转换损耗跟墙插实测能差出十来个点。软件侧要有三样东西功耗采样脚本、负载生成器、结果汇总表。负载生成器要能控制请求到达模式、输入长度分布、输出长度分布这三个参数决定了测试结果能不能代表真实场景。很多人用固定长度的合成请求测出来的能效数字跟真实流量下的数字能差一倍原因就是真实流量的长度分布是长尾的。5.2 基准任务与采样窗口的设计基准任务的设计原则是“可复现、可比较、贴近真实”。我会准备三组负载短输入短输出模拟分类、抽取类请求、长输入短输出模拟文档问答、短输入长输出模拟生成类请求。三组分别测不要混在一起算平均值混在一起会掩盖掉某一类的严重退化。采样窗口按前面说的方法定预热到稳态取稳态窗口重复至少三次报告均值和波动范围。三次之间的差异如果超过 5%说明你的测试环境不稳定可能是共享机器、可能是散热条件变化先去解决稳定性问题再谈优化效果。另外建议固定环境温度记录。同一台机器在冬天和夏天测出来的能效可以差 5% 以上这个差异来自风扇转速和降频策略不是你优化的功劳。报告里带上环境温度自己心里有数别人看了也能判断数据可不可比。5.3 结果呈现帕累托前沿比单一分数有用得多能效评估最常见的错误是为了一个漂亮的数字牺牲掉别的维度。我的做法是画帕累托前沿横轴是能效tokens/J纵轴是质量指标准确率或者人工评分每个配置是一个点把不被任何其他点同时支配的点连起来那条线就是你的可选方案集。配置tokens/J相对基线质量指标变化延迟变化结论基线1.0000参照权重低比特量化1.12-0.8-15%可接受量化 连续批处理1.35-0.8-8%推荐激进量化1.48-4.2-22%长尾任务掉点明显慎用蒸馏小模型1.9-2.1-40%视业务容忍度这张表比“我们省了 35% 的电”有说服力得多因为它同时给了代价。做技术选型的人最怕的就是只看到收益看不到代价最后上线才发现质量掉了回滚的成本远高于当初省下的电费。6. 踩坑实录让能效数字失真的六件事6.1 温度墙和降频跑了半小时数字就变了这是我踩过最隐蔽的一个坑。短时间测试时机器冷、风扇还没拉满功耗和频率都在高位能效数字好看。跑满半小时后散热达到平衡芯片温度顶到阈值频率自动下调功耗曲线整体下移一截。如果你只在开头采了几分钟数据得出的结论会系统性偏乐观。解决办法很简单稳态窗口至少 20 分钟且必须用功率曲线的标准差来判断是否真的稳态。我一般会额外记录频率和温度如果频率在窗口内出现阶梯式下降说明已经撞墙这个窗口的数据要重测或者标注。6.2 空载功耗只测高峰不看低谷线上服务不可能一直满载。真实场景里凌晨的流量可能只有白天的一成但那段时间卡还在待机耗电。做能效评估时如果只取高峰窗口算出来的“单位 token 能耗”会显著低于全天平均值用来做容量规划会低估总电费。我的做法是按真实流量曲线做一次全天的加权把一天切成若干时段每个时段按实际流量占比加权计算能耗。这个数字才是你下个月电费账单的预期值。做优化决策时用这个加权值做技术对比时用稳态值两套数各有用途别混。6.3 预热偏差与基准刷分有些测试框架会在测量开始前跑一段“热身”这段热身如果用了和正式测试相同的数据模型可能在热身阶段就把缓存填满了正式测试时命中率虚高能效数字被抬高。这个问题在带缓存的检索增强场景里尤其明显。还有一种更常见的偏差测试集和训练集有重叠或者提示词模板被反复调优到刚好适配测试集。这类问题不会直接体现在能效数字上但会让“质量指标”虚高从而让你误以为某个更激进的优化方案是可接受的。我现在的习惯是留一份完全没参与过调优的保留集只在最终验收时用一次。6.4 量化掉点被平均值掩盖平均准确率是最会骗人的指标。一个量化配置在整体测试集上只掉 0.8 个点看起来完全可接受但拆开看长尾类目掉了 8 个点特定长度的输入掉了 6 个点。这些退化在平均值里被大量简单样本稀释掉了上线之后用户感知到的就是“某些问题突然答不准了”。所以我在评估量化时一定做分层按输入长度分桶、按任务类型分桶、按难度分桶每桶单独看。任何一桶的退化超过阈值我用的阈值是 3 个点就要单独分析原因而不是用总体平均把它盖过去。6.5 结论外推的边界换个批大小就不成立能效结论的可迁移性比想象中差。你在批大小 8、序列长度 512 的条件下测出的“量化提升 12%”搬到批大小 64、序列长度 4096 的场景里可能完全不成立甚至方向相反。原因是瓶颈变了小批短序列是带宽受限大批长序列是算力受限两种情况下量化的收益逻辑完全不同。我的做法是给每个能效结论标注适用边界批大小范围、序列长度范围、并发水平、硬件型号档位。报告里写清楚“本结论在 A 到 B 的区间内成立”比给出一个孤零零的百分比负责任得多。别人拿去做决策时也知道自己有没有越界。6.6 只优化不验证改完不复测等于没改最后一条听起来很傻但真的很常见。改了一个配置觉得理论上应该更省就直接写进报告没有重新测量。理论推导在能效这件事上经常失灵因为整个系统里有太多相互作用的环节。我现在的规矩是任何进入报告的能效数字必须有对应的实测记录记录里要有时间、环境温度、硬件状态、测试脚本版本。没有这些数字就只是一个猜想。7. 把能效数据变成决策优先级排序与表述分寸优化手段那么多先做哪个我用一个很朴素的排序方法把每个候选手段的“预期能效收益”和“实施成本”放在一张二维表上优先做收益高、成本低的。按我的经验排在前面的一般是这几类输出长度控制、连续批处理、请求调度与错峰、缓存复用。这些的共同点是改动在系统层不动模型本身风险低、见效快。量化和蒸馏排在后面因为它们要动模型需要完整的质量验证流程周期长。排在最后的是硬件层面的改造比如换散热方案、调整机房气流组织。这类改动能效收益可能不小但决策周期长、涉及面广不适合作为快速见效的手段。表述分寸这块我有两条自己的规矩。第一条收益说区间不说点值。“本方案在测试条件下带来 10% 到 15% 的单 token 能耗下降”比“本方案节能 15%”更可信也更经得起追问。第二条口径、边界、代价三件事必须同时出现。只报收益不报代价的能效报告短期好看长期会透支信任——一旦有人复现不出来后面所有数据都会被怀疑。我个人在实际操作中的体会是能效这件事最难的不是技术是坚持测量。优化手段本身在公开资料里都能找到真正拉开差距的是你有没有一套稳定的测量流程能不能在每次改动后老老实实复测一遍能不能把不好看的数据也留在报告里。我做过的一个项目前期花了将近三分之一的时间在搭测量链路上当时觉得慢但后面每次迭代都能在半天内拿到可信的对比数据整个优化周期反而缩短了不少。如果你刚开始做这块我的建议是先别急着上手段花一周把采样脚本、负载生成器、结果表这三样东西搭稳后面会省下无数返工。