返回博客

2026.11.02

功耗墙从 200 瓦放到 250 瓦只快 1.4%,同样 200 瓦欠压却快 11.4%:一张被功耗墙卡住的卡,到底该拧哪个旋钮?

一张矿改的数据中心卡,我把功耗墙放宽 50 瓦,算力只涨 1.4%,因为它平均只多吃了 4.6 瓦。换个方向,功耗不动、给频率曲线加偏移欠压,同样 200 瓦多出 11.4%。可放回真实负载里只剩 7.2%,而我第一次报的是 28%,错在时区。这篇讲怎么判断你的卡被哪堵墙卡着、怎么从微基准估端到端收益,以及调频工具和我自己的监控脚本各骗了我一次。

硬件GPU功耗实测

我把一张卡的功耗墙从 200 瓦调到 250 瓦,满载矩阵乘从 162.5 TFLOPS 变成 164.83。多给了 50 瓦,快了 1.4%。

然后功耗墙放回 200 瓦,只改了一个频率偏移,同样的测试跑出 181.0。功耗 199.3 瓦,几乎没动,快了 11.4%。

再然后我把它挂回自己那个跑了好几周的模拟任务,算端到端收益,报了一个 +28%。可盯着曲线看,怎么都看不出有两成。回去重算,真值 +7.2%。那 20 个点是时区差出来的。

这篇就讲这三个数之间发生了什么。想直接要判据的看第 2 节和第 4 节,各是一个除法;想看我怎么被两个工具骗的,看第 5 节。

1. 先看清楚:卡你的是哪堵墙

这张卡是数据中心卡的矿改版,被动散热,我自己做了导风罩(之前那篇讲过那个罩子怎么从 92 度做到 64 度)。罩子改完以后它在 200 瓦下稳在 81 到 83 度。

一张卡掉频,通常就两种原因:太热了,或者电给够了。很多人一看频率低就去加风扇,或者反射性地把功耗墙往上拉。这两招对的是两种不同的病。

NVIDIA 的卡自己会告诉你是哪种:

nvidia-smi -q -d PERFORMANCE

我那天读到的是:

SW Power Cap        : Active
SW Thermal Slowdown : Not Active

功耗封顶在生效,温度降频没在生效。十秒采样里功耗 195 到 207 瓦,每一秒都贴着 200。说人话就是:卡不热,它是饿的,电就给这么多,它只能跑这么快。

好笑的是,我前几轮一直拿旧罩子时代的热学结论解释这张卡(那时候它确实是被温度卡的),散热改了三次,限制它的东西早就换人了,我还在治发烧。

2. 放宽功耗墙,为什么只多 1.4%

按「饿」的逻辑,多给电应该多干活。实测:

功耗墙 实际平均功耗 bf16 算力
200 W 199.3 W 162.5 TF
250 W 203.9 W 164.83 TF

墙抬了 50 瓦,它只多吃了 4.6 瓦。峰值偶尔能摸到 256,均值就是上不去。

判断这一招值不值,先算一个兑现率:

功耗墙兑现率 = 实际平均功耗 ÷ 功耗墙设定值
203.9 ÷ 250 = 81.6%

兑现率远低于 100%,说明这张卡在这个频率曲线上根本吃不满新给的电。剩下那 46 瓦只是一张没兑现的支票。卡在某个频率上要的电压是固定的,电压曲线不变,频率就上不去,给它再多瓦它也用不上。

后来我在自己的真实负载上又做了一次 250 对 200 的交替 A/B:250 瓦快 2 到 3%,方向 8 次全一致,但功耗多 15%,温度高 3 度,降频时间从 0 涨到 64% 到 100%。250 瓦自己两轮之间就差 2.4%,这个「快 2 到 3%」的幅度精度其实不够,方向可信,数字别抄。

结论很朴素:功耗墙留 200。

3. 欠压:同样的瓦数换更高的频率

有个开源项目专门调这类卡,它走的路是给频率-电压曲线整体加一个偏移。直白点说,告诉卡「同样的电压,你可以跑得比出厂设定快 X MHz」。出厂曲线给的电压余量通常偏保守,偏移就是在吃这部分余量。

在一张被功耗墙卡住的卡上,这件事的效果特别直接:同频率要的电压低了,每瓦能换到的频率就高了,而功耗墙还是那堵墙。

功耗墙 200 瓦,停掉所有服务独占跑,每档 15 秒:

偏移 bf16 算力 功耗 核心频率 对比出厂 最高温
+0 162.5 TF 199.2 W 1215 — 81-82
+100 174.3 TF 199.0 W 1303 +7.3% 77
+150 178.1 TF 199.0 W 1333 +9.6% 79
+200 181.0 TF 199.3 W 1355 +11.4% 80
+250 184.3 TF 198.8 W 1380 +13.4% 81
+300 188.9 TF 197.7 W 1412 +16.2% 81

同样 200 瓦,+300 多出 16.2%,温度还低了 1 度。频率从 1215 爬到 1412,功耗一格没涨。

把第 2 节和这一节放一起看:同一张卡,往上加 50 瓦换 1.4%,原地欠压换 11.4%。在功耗墙生效的卡上,该拧的是电压曲线。

3.1 我最后挑了 +200,没挑 +300

+350 那档直接崩了,驱动报 Xid 13。Xid 是 NVIDIA 驱动往内核日志里写的错误码,13 号是图形引擎异常,进程死、卡不死。所以这张卡的墙在 +350。

选点我用的是这个账:

离崩溃线的余量(档)= (崩溃档 − 选择档) ÷ 步长
+300:(350 − 300) ÷ 50 = 1 档
+250:(350 − 250) ÷ 50 = 2 档
+200:(350 − 200) ÷ 50 = 3 档

+300 多 4.8 个点,只剩一档余量。那个开源项目的作者在自己的参考卡上写得很清楚:+300 那档过了四轮全显存扫描,跑了一整天基准,然后在一个小时级的长时间压测里炸了;他前后有三个配置都是这个下场,最后自己用的也是 +200。

欠压这种事,最怕的崩法是不崩:不报错,就是偶尔算错一个数。对一个要连跑几周的模拟,悄悄算错比崩掉贵得多。用 2 个点换一整档余量,我觉得划算。

挂上 +200 以后在真实负载下跑了 225 分钟,内核日志里的 Xid 计数从头到尾没变,功耗 179 到 235 瓦之间跳(墙是 200,瞬时采样会超),核心 82 到 83 度,限制因素全程还是功耗封顶。

4. 微基准快 11.4%,端到端只有 7.2%

接下来是我翻车的那段。

4.1 那个 +28%

我从调用日志里切了两个时间窗:偏移生效前一段,生效后一段,比每秒产出的 token 数。算出来 +28%,挺高兴。

然后我盯着曲线看了一会儿,怎么都看不出有 20%,心里开始发虚。

查下来是时区。偏移在本地时间 20:32 生效,那天是夏令时,本地比 UTC 慢 4 小时,所以生效时刻是 UTC 00:32。日志库存的是 UTC,我写的切分条件却是 UTC 02:33,晚了整整两个小时。B 窗口只取到了最后 13 分钟,那 13 分钟又刚好撞上一批重活,那段时间平均每条请求的 token 数是 1169,平常大约 350,数字就被撑上去了。

这个坑我的笔记里早写过:数据库是 UTC,系统日志和文件时间是本地时间。我甚至在那一轮开头刚说完「先把时间线钉准」。笔记里写了不等于会照做,这个我到现在也没想出好办法,只能把「换算后的起止时间回显出来再看一眼」写进每个对比脚本里。

4.2 正确的数

同口径,15 分钟一个桶,只看这一张卡:

桶数 每桶请求数 每秒产出 token 平均输入长度 平均输出长度
出厂 15 374.2 143.9 27277 355
+200 9 383.0 154.2 28131 369
端到端增益 = 154.2 ÷ 143.9 − 1 = 7.2%

两段的工作量对得上,输入长 3.1%,输出长 3.9%,都在噪声里。

为什么看图看不出来?逐桶的产出在 103 到 203 之间来回跳,桶和桶之间的自然波动比 10.3 tok/s 的提升大了一个数量级。7% 这种量级,肉眼盯曲线必然看不出来,只能聚合了算。所以「看着没提升」和「其实提升了 7.2%」两句话都是对的,错的只有我那个 28。

4.3 从微基准估端到端

传导率 = 端到端增益 ÷ 微基准增益
7.2 ÷ 11.4 ≈ 0.63

微基准测的是纯矩阵乘满载,频率涨多少它就涨多少。真实推理里还有注意力、kernel 启动、调度、网络、排队,这些不全吃核心频率。所以只有六成多传导过去,我觉得合理。

这个数你可以反过来用:下次看到别人说「欠压/超频让我的卡快了 15%」,先问是哪个口径。如果是矩阵乘微基准,代一个 0.6 左右的传导率,端到端大概也就 9% 上下。我的 0.63 只是一张卡、一种负载上的一个点,你的负载如果更偏算力密集会高一些,更偏访存或调度会低一些。

5. 两个工具,各骗了我一次

5.1 资格认证工具把一次崩溃吞掉了

那个开源项目带一个「资格认证」命令:从低到高逐档爬偏移,每档跑扫描,最后退一整档给出推荐值,写一个「已认证」的回执。我跑完它,终端干干净净,推荐 +250,四轮全显存扫描全过、几万次矩阵乘零错误。

然后我去翻内核日志,看到爬台阶的时候 +350 那档炸过一次 Xid 13,进程名就是它自己的测试程序。

原因在脚本写法:爬台阶那一步的输出被外层用 $(...) 捕获了,只有最后一行「最后一个过关的档」冒出来,中间哪一档、怎么炸的,全被吞掉。终端上你只能看到它从容地进入了「确认推荐值」阶段。

所以跑完这类工具,别看它的标准输出,单独查一遍:

journalctl -k | grep -i xid

顺带两个它没告诉你的副作用:认证命令里写死了把功耗墙设到 250,爬台阶的子命令又会自己设一遍功耗,跑完卡上留着的是 300 瓦的墙和 +250 的偏移,它不还原。还有个开机自检服务装完默认启用,也写死了 250 瓦,跟我自己设 200 瓦的开机服务没有先后约束,谁赢看运气。我把那个服务关了。

5.2 我的监控脚本数了 225 分钟的「零」

长时间压测那段,我写了个监控,每分钟记一次新增 Xid、显存温度、当前偏移,任何一项异常就自动回滚到出厂。

第一版跑起来,三个字段全是坏的:

  • Xid=0:它在 systemd 服务里跑,那个环境下 dmesg 读不到内核缓冲区,静默返回空,计数就是 0。真实基线是 42。
  • hbm=C:显存温度的字段名我 grep 错了,取到一个空值后面跟个单位。
  • off= 空:输出里有一个 Success 单词挡在数字前面,取错了位置。

最危险的是第一个。「零新增 Xid」和「根本没在数」长得一模一样,要不是看到另外两个字段明显是空的,我会安心看它数一下午的零。修完以后我在脚本开头加了一行自检,先把当前 Xid 总数打出来,看到 42 才说明这个判据是活的。

6. 拆穿一个跨卡对比

那个开源项目的作者也有一张同型号的卡,他的出厂成绩是 184.3 TF。我的出厂是 162.5,乍一看我这张只有人家的 88%,很容易得出「我抽到了一块烂硅片」。

162.5 ÷ 184.3 = 0.88

但他那张卡没设 200 瓦的功耗墙。我这边 +250 偏移就是 184.3,跟他出厂一模一样。差距全在工作点上,硅片没毛病。

所以看到任何「同一张卡我比别人慢 X%」,先对齐三件事:功耗墙一样吗,频率偏移一样吗,测的是满载还是端到端。三件事有一件没对齐,这个 X% 就没法读。同一类的还有「我的卡被温度卡着,你的被功耗卡着」,同一个设置在两张卡上效果可以完全相反。

7. 没做完的,先说在前面

  • 只有一张卡、一种负载。 63% 的传导率换个模型、换个并发度会变。
  • 225 分钟不等于长期稳定。 原计划 240 分钟,最后 15 分钟因为要搬机器提前停了。参考卡的经验是有的配置要到小时级才炸,几天、几周的数据我没有。而那种「悄悄算错」的失败,Xid 计数根本数不到,我目前没有手段验证它不存在。
  • 端到端那组样本不对称。 出厂 15 个桶,+200 只有 9 个桶,中间还穿插了停服做基线的时段。7.2% 能信方向和量级,小数点后那位别太当真。
  • 两次频率都涨了 11% 左右,结果差很远。 250 瓦那次核心频率涨 11.3%,长 prompt 预填充只涨 2.4%;欠压这次频率涨 11.5%,端到端涨 7.2%。两边口径不同,一个是单条请求预填充的中位数,一个是 15 分钟桶的总产出,我没做同口径对照,这里不下结论。
  • 认证回执和实际工作点对不上。 回执是在 250 瓦下跑出来的,我实际用在 200 瓦。电压余量跟着工作点变,严格来说应该在 200 瓦下单独认证一次,我还没做。
  • 机器后来搬了房间,环境温度变了,散热那套热阻还没重测,所以「200 瓦下 81 到 83 度」只对搬家前有效。

8. 下次照这个顺序来

  1. 跑 nvidia-smi -q -d PERFORMANCE,看是 SW Power Cap 还是 SW Thermal Slowdown 在 Active。温度在卡就先治散热,功耗在卡再往下走。
  2. 抬功耗墙之前,先在新墙下满载跑一次,算 实际平均功耗 ÷ 设定值。兑现率远低于 100% 就别抬了,抬了也只是多一份热。
  3. 功耗墙生效的卡,试频率偏移。功耗墙固定,从小往大一档一档爬,每档同时记算力和功耗。
  4. 找到崩溃档以后往回退,用 (崩溃档 − 选择档) ÷ 步长 算余量,至少留两档。要跑长任务就留三档。
  5. 任何调频或认证工具跑完,单独 journalctl -k | grep -i xid,别信它的终端输出。也查一遍功耗墙和偏移有没有被它改了没还原。
  6. 长时间压测的监控脚本,开头先打一行当前计数的绝对值。看到一个合理的非零基线,再相信后面那一串零。
  7. 算端到端收益时,切窗口前把起止时间换算成日志库用的时区回显一遍。然后用 微基准增益 × 传导率 先估一个预期,实测比预期高出一大截的时候,先怀疑窗口切错了。