pixelcluster 的 GPU 博客有一篇《Beyond the Limits of Physical VRAM》,讲的是显存管理补丁经过数月 mailing list 拉扯之后终于进了主线,排进 Linux 7.3。作者前一篇讲的是改进游戏显存管理,留了一句尾注:只要游戏用的显存不超过你实际拥有的量,表现会稳定很多。这篇就是来处理"超过"这个前提被打破的情况。

这篇值得读的地方在于它把一个很具体的工程问题拆成了带宽和延迟两个可算的量,而不是笼统地说"显存不够就卡"。我把它里面的账重新算了一遍,顺便补上几个没写出来但对开发者更要紧的推论。

问题:显存不够会发生什么

显存不够时,内核要把一部分缓冲区搬到系统内存里,等到 GPU 需要访问时再换回来。这个机制在概念上跟 CPU 的换页很像,但代价完全不同,因为 GPU 访问的带宽和延迟特性和 CPU 相差一个量级。

CPU 换页的时候,被换出的页面通常很久不会再被碰,这是个"冷访问"模式,值得用一次几十微秒的缺页代价去换。GPU 不一样:一个纹理被换出后,下一帧可能立刻就要用,这是"热访问",缺页代价直接落在帧时间上。所以显存超分和内存超分在工程上不是同一类问题——内存超分能靠换页混过去,显存超分混不过去。

他给的最小账是这样的:假设 GPU 通过 PCIe 4.0 x16 连接,可用带宽略低于 32GiB/s。如果按 30 帧每秒的最低可玩标准,每帧的预算是 33.3 毫秒。在这段时间里,GPU 最多能访问多少数据?

带宽乘以时间:32GiB/s × 33.3ms ≈ 1.07GiB。也就是说,一帧之内,凡是超出片上缓存、需要走 PCIe 的数据,总量不能超过一个吉字节左右。这不是"大概",是从带宽直接推出来的硬上限。

如果你的场景需要在一帧内访问 2GiB 的纹理,那不管你怎么优化 kernel,都做不到 30 帧——瓶颈在总线,不在算力。这正是"显存超分"必须小心的地方:你可以让程序不崩,但没法让它在超分状态下保持帧率。

延迟的层级:缓存才是分水岭

带宽是总量约束,延迟是单次访问的约束,两个都要看。

他提到一个对比数字很有意思:在 6MB 这个尺寸下——恰好是 RDNA3 的 L2 缓存大小——访问 CPU 内存的延迟升到约 2400 周期每次,而访问设备内存(显存)的延迟反而落在同一量级内。

这个观察推翻了一个直觉。很多人以为"显存比系统内存快得多",但在大块数据超出缓存之后,两者对 GPU 的延迟其实差不多,都是几百到几千周期。真正的分水岭不是"在显存还是在内存",而是"在不在缓存里"。

所以优化的抓手是让数据在 L2 里被反复利用,把访问次数压下来,而不是把更多数据搬进显存。6MB 这个数字之所以关键,是因为它是能被片上缓存容纳的上限——超过它,你就从延迟主导切换到带宽主导,性能曲线会有一个明显的拐点。

这里还有个容易被忽略的点:2400 周期这个量级意味着,如果你在一帧里制造了几十万次缓存未命中,把这些周期加起来,就足以吃掉整个帧预算。所以真正的敌人不是"某一次访问很慢",而是"访问次数太多"。

那 Linux 7.3 的补丁做了什么

作者没有在博客里逐条列 patch,但从他的描述能读出方向:让内核在显存压力下的换入换出行为更可预测,减少游戏因为显存超分而出现的突发卡顿。前一篇的结论是"不超分就稳定",这一篇则是在"不得不超分"时把伤害控制住。

这个转向本身值得注意。早期的显存管理思路常常试图把超分做"透明"——让应用完全不知道显存不够。但按上面的账,透明是不可能实现的:一帧的数据预算被 PCIe 带宽钉死,内核再聪明也变不出带宽。能做的只是让换出策略更平滑,让卡顿从"每隔几秒一次的大停顿"变成"持续但可接受的轻微掉帧"。

从内核工程的角度看,这类补丁能进主线,通常意味着它放弃了一些不切实际的目标。不再承诺"超分也流畅",而是承诺"超分时的行为有界、可预测"。对驱动和应用来说,可预测的降级比不可预测的峰值更有价值——前者能被调度器纳入考虑,后者只能靠运气。

给图形/计算开发者的启示

从这篇里能抽出几条对实际开发有用的判断:

第一,先算你的 working set 有多大,再看它落不落得进 L2。落得进,你是延迟问题;落不进,你是带宽问题。这两个问题的解法完全不同,混为一谈会优化错方向。延迟问题靠提高缓存命中率和减少访问次数解决,带宽问题只能靠"减少要搬的数据总量"解决——降精度、降分辨率、换压缩格式,都是这一类。

第二,用带宽反推帧预算。PCIe 4.0 x16 不到 32GiB/s 这个数是硬约束,把它乘上你的目标帧时间,得到每帧可访问的数据上限。如果你的纹理总量超标,先减数据,别减计算。计算可以靠并行度堆出来,带宽不行。

第三,别指望内核帮你兜底。显存超分能救的是"不崩"和"稳定",救不了"高帧率"。在设计资产管线时就要按显存上限规划纹理分辨率,而不是上线后靠驱动调参。资产做大了再降,美术返工成本远高于一开始就规划。

我的做法

做 GPU 相关的性能工作,我现在会先做两件事再谈优化:

  • nvidia-smi 或 radeontop 看显存占用曲线,确认峰值有没有超过物理显存。超过就是带宽问题,优先级最高,其它优化全部往后排。
  • 用 L2 大小作为切块粒度。RDNA3 是 6MB,把计算切成能反复命中 L2 的块,比盲目加大 batch 更有效。判断标准很简单:如果加 batch 之后帧率反而掉了,说明 working set 已经越过缓存边界。

这类补丁的价值不在它让超分变快,而在它把"超分时到底发生什么"变得可观测、可解释。可解释的卡顿比随机的卡顿好修得多。