RTX 5090 的细粒度温度传感器与物理 SM / TPC 映射探测

 

七月初,超频爱好者们又给老黄带来了新的惊喜:RTX 5090 的若干非公开的温度传感器的 PCIe BAR 地址被逆向了出来: [1][2][3]。这些传感器包含了四个热点(hotspot)CH0 到 CH3,以及每一片 GDDR7 显存模块的温度(根据型号不同,可能有 8 或者 16 个传感器)。这些热点传感器比公开 API(比如 nvidia-smi 或者 NVML)能读到的信息更丰富,更接近芯片的实际温度(比如结温),刷新率也更高,自然能给超频玩家更多的玩法。但我并不超频,它还有什么用呢?有的,这两天,在 GPT-5.6 Sol 的帮助下,我还是做了一些有趣的探究。

注:以下大部分实验是 AI 做的,结果数据、图表由 AI 收集生成,我已尽力进行审阅。

背景:传感器信息

5090 的 PCIe BAR 中,如下的地址有六个核心温度寄存器:

  • 0xAD0A90..0xAD0A9C:四个独立通道 CH0-CH3
  • 0xAD0AA0:硬件聚合最大值(Hotspot)
  • 0xAD0AA4:硬件聚合平均值

它们都使用低 16 位,以 1/256 摄氏度为单位的定点数表示温度。此外,还有 8 或者 16 个寄存器保存了当前的显存颗粒温度,格式和地址范围都比较复杂。这些寄存器具体的读取逻辑都可以参见 gpuwatch 项目。此外还需注意,还需要打开 iomem=relaxed 内核命令行选项,才能从用户态映射 PCIe BAR 空间。

实验:负载与温度变化

一些简单的 CUDA 负载测试结果:

负载 功耗 W Hotspot C 硬件平均 C CH0 CH1 CH2 CH3 显存最高
空闲基线 22 26.4 24.1 26.3 22.1 23.4 26.4 32.0
FP32,1 block 102 37.9 33.2 37.9 30.9 29.4 33.2 33.7
FP32,22 blocks 137 57.2 37.6 57.2 33.6 33.1 36.2 35.0
FP32,44 blocks 188 71.3 42.8 71.3 36.3 36.2 40.6 37.0
FP32,88 blocks 284 84.0 54.4 84.0 44.4 40.7 55.7 40.1
FP32,170 blocks 421 104.2 61.9 104.2 48.6 42.7 60.6 44.2
FP32,680 blocks 414 104.2 61.9 104.2 48.6 42.9 60.5 44.8
INT32,全负载 414 104.0 61.9 104.0 48.4 42.7 60.1 44.8
L2,32 MiB 328 71.4 55.9 71.4 45.6 42.2 54.8 43.8
GDDR7,2 GiB 319 65.1 51.7 65.1 44.4 40.8 49.3 51.5
Tensor Core 449 93.0 62.9 93.0 51.0 45.3 59.5 51.5

简单观察这些 CH 传感器的变化,可以得到如下结果:

  1. Hotspot 是各通道最大值,而计算负载几乎总会使 CH0 成为最大值(远超平均温度,甚至超过 100 度)。
  2. 几乎总是有 CH0 > CH1 > CH3 > CH2。
  3. 读到的 “平均温度”并不是四个通道的简单平均,但是与功耗的相关性更高,可能是某种加权平均。

接着进行了一些脉冲测试,也就是启动较短时间的 kernel,观察温度变化,结果如下:

脉冲长度 Hotspot 增量 C 硬件平均增量 C Hotspot 峰值时间中位数
50 ms 13.9 8.6 27 ms
100 ms 25.5 13.5 90 ms
200 ms 36.0 18.3 197 ms
500 ms 50.0 24.7 508 ms
1 s 59.8 29.5 981 ms
2 s 64.9 30.4 1.994 s
5 s 75.7 33.8 4.916 s
10 s 74.6 31.2 5.032 s

看起来 Hotspot 的测量非常灵敏。在 kernel 结束后,其下降也非常快。通过更多的测试,可以确定这些传感器的更新频率在 50Hz 左右。

接下来,在单个固定的 SM 上,测试了 32-1024 个线程、1-6 个 256 线程 block,以及 32x8、64x4、128x2 等 warp 总数相同的配置。单 warp 场景消耗 99.6 W;其他所有场景约为 100.6-102.5 W。除第一个、温度最低的冷态实验以外,每种配置扣除基线后的 CH0 增量都是 8.89-9.04 C。其他通道同样几乎不变。也就是说,空闲功耗以上约 80 W 的大部分增量都来自使整个设备进入高性能时钟/功耗状态。只改变 170 个 SM 中一个 SM 的工作量,影响太小,几乎无法通过温度传感器区分。

注:此处的“单个 SM”并不是说我们能控制 CUDA 程序运行在某个物理 SM 上。事实上,我们总是可以占满 GPU,并通过读到的 SM ID 决定来哪些 SM 实际运行负载,让剩下的 SM 处于空闲状态。下面的实验均使用了类似的方法:

smid = read_special_register("%smid");
if (!active_sms[smid])
    return;
selected = atomicAdd(&per_sm_counter[smid], 1) < 4;
if (!selected)
    return;
while (globaltimer < deadline) {
    // 大量寄存器内 FP32 FMA
}

也就是说,最先抵达这个 SM 的四个 block(每个 256 线程,已经能足够打满 SM 的功耗)会一直执行,直到全局计时器到达截止时间。

实验:温度与降频

可以确定的是,CH0 的温度参与了 GPU 对温度降频(thermal throttling)的决策。在一个持续 90 s、使用 170 个 block 的 FP32 负载中,第一次报告硬件温度降频事件是在 kernel 启动 6.50 s 后可观察到:

  • 隐藏 hotspot:99.94 C
  • 硬件平均值:59.69 C
  • 驱动 GPU 温度:59 C
  • 板卡功耗:446 W
  • 图形时钟:2775 MHz

此时驱动温度还比较低,只有 60 度,但 GPU 已经开始报告温度降频事件发生。随后过一段时间,hotspot 保持在约 104.5 C,而负载的三个三等分区间变化如下:

区间 Hotspot 平均 C 硬件平均 C 功耗 W 图形时钟 MHz
前 30 s 103.3 61.2 421.6 2719
中间 30 s 104.6 63.0 399.0 2597
后 30 s 104.6 63.5 391.6 2570

可以推测,当 hotspot 超过 100 度后,就可能触发降频保护机制。此时 GPU 整体(或者说外界可见)温度和功耗都远远没有达到极限。

我还尝试了基于 hotspot 温度的简单计算控制,也就是在温度即将到达阈值时暂停计算,来试图避免降频,获得更高的计算性能。然而由于同步和调度等开销,这种“占空比”的控制方法并没有获得太好的效果。

实验:温度与 SM 映射

由于单活动 SM 的实验没有什么效果(背景温升盖过了单个 SM),我选择用一组 42 个 SM 来测试(这个数字是随便选的,大致是 170 个 SM / 4 个传感器),每次只在一组上施加负载。除了连续的四组 SM 之外,还有一个实验,是每隔四个 SM 选一个,同样有 42 个。结果如下:

SM ID 集合 稳态功耗 W CH0 温升 C CH1 温升 C CH2 温升 C CH3 温升 C 平均温升 C
q0,0-41 188.24 50.80 13.75 11.76 12.88 18.98
q1,42-83 191.15 33.95 12.23 9.38 19.86 19.72
q2,84-125 191.34 44.83 12.96 9.57 14.21 18.86
q3,126-167 189.45 29.76 11.15 8.94 23.19 19.59
条纹,0,4,…,164 189.92 36.60 13.40 9.23 18.41 18.24

很明显,不同的活跃 SM 集合对不同的传感器影响不同。因此我猜测这些传感器在物理上的分布并不一致。

实验:温度传感器对少量 SM 负载的响应

我让 Codex 用不同的组大小进行了充分的扫描,发现在组大小为 1 或者 2 的时候,对传感器的影响最为显著。

sensor-response-to-sm-group

需要注意的是,这张图里的线代表了“传感器的温度变化减去基线温度变化”的值,也就是说,纵轴的零点代表了某个传感器在负载开始前的温度均值。从图中可以看到,CH0 对某几个 SM 的响应非常明显,CH3 也有一些点比较明显,并且这些点的编号似乎有某种周期性。这些信号都增强了我的猜测,即这些温升的变化,和传感器与 SM 的物理距离有关系。

然而,想要直接弄明白传感器的位置,还需要先知道 SM 的物理位置,这还需要更多的实验。

实验:SM ID 到 GPC/TPC 的映射

根据如下图中 5090 的 die shot,它由 12 个 GPC 组成,每个 GPC 上有 8 个 TPC,每个 TPC 上又有两个 SM。虽然总共有 192 个 SM,但由于良率或者其他原因,实际只启用了 170 个。根据分析,其中有 3 个 GPC 只启用了 7 个 TPC,有 8 个 GPC 启用了全部的 8 个 TPC,还有一个 GPC 完全被禁用。

rtx-5090-dieshot

因此,第一步是把 SM ID 映射到 GPC 中,才能对应到 die shot。这一步并用不到温度传感器。

CUDA 编程模型保证,thread-block cluster 中所有 block 会同时调度在同一个 GPC。因此只要让 cluster 内每个 block 读取 %smid,就可以把这些对应的 SM ID 归类到同一个 GPC 中。经过充分的数据获取和处理处理(一些简单的图上连通分量分析),可以获得 11 个逻辑 GPC 对应的 SM ID 集合:

逻辑 GPC TPC 数 SM 数 每个 TPC 的 SM ID 对
0 7 14 0/1, 22/23, 44/45, 66/67, 88/89, 110/111, 132/133
1 7 14 2/3, 24/25, 46/47, 68/69, 90/91, 112/113, 134/135
2 7 14 4/5, 26/27, 48/49, 70/71, 92/93, 114/115, 136/137
3 8 16 6/7, 28/29, 50/51, 72/73, 94/95, 116/117, 138/139, 154/155
4 8 16 8/9, 30/31, 52/53, 74/75, 96/97, 118/119, 140/141, 156/157
5 8 16 10/11, 32/33, 54/55, 76/77, 98/99, 120/121, 142/143, 158/159
6 8 16 12/13, 34/35, 56/57, 78/79, 100/101, 122/123, 144/145, 160/161
7 8 16 14/15, 36/37, 58/59, 80/81, 102/103, 124/125, 146/147, 162/163
8 8 16 16/17, 38/39, 60/61, 82/83, 104/105, 126/127, 148/149, 164/165
9 8 16 18/19, 40/41, 62/63, 84/85, 106/107, 128/129, 150/151, 166/167
10 8 16 20/21, 42/43, 64/65, 86/87, 108/109, 130/131, 152/153, 168/169

之所以称为“逻辑 GPC”,是因为我们依旧不知道它的物理位置,只是赋予了一个唯一编号(按照 SM ID 排列)。这与上面的 die shot、以及温度传感器对 SM 的响应分析能互相验证:编号相邻的两个 SM 对温度的影响几乎是相同的,因此应当确实属于同一个 TPC。

我们将每个 TPC 中的两个 SM 视作一个整体,用上述的逻辑 GPC、TPC 编号,绘制所有温度传感器对不同 SM 的响应图示:

die-layout

显然,从这张图中,我们已经能获得很多有用的信息,甚至已经可以进行一些基于人工智能(指人脑)的重新排布。但为了让结论更有说服力,我们还需要进行一些定量的分析。

分析:GPC/TPC/SM ID 到物理位置的映射

现在就剩下了最后的两个问题:

  1. 8 个 TPC 在 GPC 中的物理排布是怎么样的?
  2. 12 个 GPC 在整个 die 上的物理排布是怎么样的?

为了解决这个问题,我们可以先进行一些符合直觉的假设来缩小搜索空间:

  1. GPC 的编号应当在物理上连续;不同的 GPC 中,TPC 的排布方式应当相同;
  2. TPC 在 GPC 中的排布应该使得 SM 编号遵循一定的顺序:如从左到右、从上到下递增,相邻两个编号连续或者有固定的 stride。注:规定一种排列方向会消除很多种可能的镜像排布,我们将它们视作相同的方案。
  3. 由物理定律可知:离热源越远的地方,温升越小,并且会形成连续的温度梯度。

我们不妨假设温度传感器就位于响应最高的 SM 附近(ID=96,GPC=4,TPC=4)。接着,尝试枚举所有遵循上述编号方式约束的可能 TPC、GPC 排布方式,并对以下的指标进行优化:

  • 每个热点附近的温度梯度,也就是温升与物理距离(曼哈顿距离,单位为 SM 单位格)的相关性(Spearman 相关系数)
  • 热点附近其他 SM 诱发的 1 秒内的温升比例(温度升高值与稳态温度的比例)与物理距离的相关性。

最终,可以获得如下的“镜像列主序”的排列方案,满足所有约束,并且在上述指标上表现良好:

die-layout

也就是说,12 个 GPC 很可能是从左到右、从上到下依次编号。每个 GPC 中的八个 TPC 编号方式可能是:

TPC4  TPC0
TPC5  TPC1
TPC6  TPC2
TPC7  TPC3

如上所述,还有几种全局镜像的排布方式,我们视同等价。从这张按位置映射重绘的热力图上,我们能获得以下的观察:

  • CH0、CH2、CH3 都有比较直观的温度梯度,因此我们的排布应当是基本合理的。
  • 图上的几个标记是我推测的传感器可能的位置(CH1 无法判断)。然而这几个传感器的响应绝对幅度并不相同,因此一定还有其他的差异(比如垂直距离区别)。
  • 每个传感器对 SM0/1 对应的 TPC 都有明显高于其所在位置的响应,这意味着 SM0/1 对于这四个传感器都有更高的升温作用。目前我对此尚没有合理的解释。

事实上,还有一种更符合温度梯度的方案,是 [4,0] [5,1] [6,2] [3,7];但是这样的编号实在太扭曲,再考虑到温度测量可能带有不确定的噪音,我最终舍弃了这种方案。

总结

在 GPT-5.6 的帮助下,我(基本)成功从高精度的温度传感器读数中,推测出了 5090 的一种可能的物理 SM 排布方式。尽管这些信息或许可以从其他渠道简单地获得,也很可能还有错误,对日常使用也没有太多的帮助,但依旧是个非常有趣的过程。

附录

实验过程中还进行了一些可能有用的测试,摘录如下。

负载 稳态功耗 W Hotspot 峰值 C 硬件温度事件占比
FP32 FMA 452 107.6 1.7%
INT32 MAD 431 109.2 31.1%
FP64 FMA 167 55.8 0%
SFU sine 323 90.2 0%
共享内存 363 97.4 0%
全局原子操作 162 51.5 0%
L2 读写 324 71.5 0%
GDDR7 读写 318 65.4 0%
FP16 Tensor GEMM 446 94.0 0%