在一个昏暗的机箱里,一台 RTX 3090 GPU「唱」着经典英语儿歌《一闪一闪亮晶晶》(Twinkle,Twinkle,Little Star)的旋律。这不是灵异事件,也不是科幻电影,而是一位 AI 科学家在「整活」。这位科学家名叫 Vrushank Desai。据他介绍,机箱中的旋律是由 GPU 的电感线圈发出来的。GPU 如何发出这种声音,别急,Desai 在 X 上给出了解释。事情的起因是这样的,今年年初,Desai 花了几个月的时间学习 GPU 编程,并尝试优化《Diffusion Policy》论文中的推理。在此过程中,Desai 将去噪 U-Net 的推理时间提高到 Pytorch eager 模式的约 3.4 倍、Pytorch compile 模式的 2.65 倍。不过这次尝试让 Desai 印象最深的事情,当属这个意外发现,即 RTX 3090 GPU「唱」起了《一闪一闪亮晶晶》。虽然这个发现和扩散推理毫无关系,但在 Desai 看来,却是最有趣的事情。Desai 在 X 上激动的表示:「我能够让 RTX 3090 电感线圈使用内核(GPU 编程)在正确的频率下调节功耗来播放《一闪一闪亮晶晶》。每次内核启动都会触发 GPU 的 DC-DC 降压电感中的涌流。由电流变化引起的洛伦兹力使线圈轻微移动,如果进一步控制内核发射频率,使线圈震荡,就能把噪音控制在可听到的范围内。」不幸的是,Desai 不能让设备发出低于 2000Hz 的声音,因此《一闪一闪亮晶晶》音符都向上移动了好几个八度。进一步的,Desai 在博客中详细描述了自己发现 RTX 3090 发出声音的过程,我们接着往下看。Desai 表示,在 GPU 中,电压调节模块(VRM)负责将输入功率的 12V 电压降至约 1V,以驱动 GPU 核上的晶体管,要求是 VRM 输出的电压必须非常纯净。VRM 利用复杂的电力电子技术可以提供这种纯净的输出,其中一部分转换过程涉及电感器,这些电感器本质上充当低通滤波器。当 GPU 核的负载发生显著波动时,这些电感器会产生快速振荡的磁场(与电流变化率 dI/dT 成正比),进而诱发洛伦兹力,使线圈振动。这种现象就是导致 GPU 线圈噪音的原因。然而,这一发现,并不能让 GPU 唱歌。接着,Desai 发现了一个有趣的现象,即与运行 CUDA 图形或自定义内核相比,Pytorch Eager 模式会导致更响的 GPU 线圈噪音 ——Desai 表示甚至能听到代码运行的声音!这个发现让 Desai 感到非常困惑,因为 nvidia-smi 显示加速模式比 Eager 模式多消耗约 40W 的功率,通常来说更高的负载会产生更大的噪音。Desai 推测这可能是因为 Eager 模式在内核启动之间有更长的延迟,导致 GPU 核心负载的变化更大,从而在 GPU 的电感器中产生更强的磁场振荡。为了测试这一点,Desai 编写了一个内核,该内核可以从全局内存中执行大量加载,这是一项非常耗能的操作,并改变内核启动之间的持续时间,Desai 发现确实可以通过这种方式控制线圈噪音!Desai 开始沉迷于这个发现,即让 GPU 线圈发声的能力,并编写了一个内核程序来演奏特定的音符,因而用 RTX 3090 演奏《一闪一闪亮晶晶》的曲子诞生了。不幸的是,发出的声音无法达到较低的频率,所以所有音符都被提高了好几个八度。想要让自己的 GPU 唱歌的小伙伴可以参考以下代码:代码地址:https://github.com/vdesai2014/inference-optimization-blog-post/tree/main/part-9/gpu-piano让 RTX 3090 演奏《一闪一闪亮晶晶》只是 Desai 在研究过程中的一次意外发现。博客中,Desai 用了大量篇幅介绍了自己年初以来几个月的学习经历,通过尝试优化扩散策略的推理过程来学习 GPU 编程。博客地址:https://www.vrushankdes.ai/diffusion-inference-optimizationDesai 介绍了如何优化扩散策略,Desai 表示 GPU 具有内存层次结构,其范围从大容量、低带宽、高延迟(全局内存)到低容量、高带宽、低延迟。芯片中执行实际计算的部分(FP32 / INT8 单元、张量核等)比比特从内存存储移动到计算电路的速度快得多。这导致 GPU 工作负载变得受内存限制 —— 与计算相关的比特从内存移动到计算所需的时间比实际执行计算所需的时间更长。Desai 认为近年来计算每秒可以执行的浮点运算次数与内存存储可以提供的浮点数之间的差距越来越大。对于 Desai 研究的扩散策略推理优化工作来说,几乎所有的速度提升都来自优化内存访问模式以实现更好的内存利用率。下图表明,与内存存储(绿色)相比,FP32 计算单元 / 张量核(红色)的速度快得多。全局内存访问如此慢是有物理原因的。全局内存将位(bits)存储在 DRAM 单元中,而该单元由一个电容器和一个晶体管(控制电容访问)组成。每次访问都需要对行缓冲区预充电以达到中性线电压,将需要访问的行连接到行缓冲区,选择要读取的正确的列,并将数据传输到总线。所有这些步骤需要花费大量时间来执行。因此,最有效的 GPU 性能优化手段之一是从全局内存加载数据时访问连续存储器地址。DRAM 的物理结构是其发挥作用的原因。由于每次访问一行需要将该行所有的位拉入到行缓冲区,因此同时访问彼此相邻的多个位是高效的做法。DRAM 的优点是,虽然速度相对较慢,但成本低并且易于密集封装,毕竟只需要一个电容器和一个晶体管。如下为一个 DRAM 单元的 SEM(扫描电子显微镜)图像。在存储器中存储一个 FP16 GPT-4 实例需要大约 30 万亿个这样的单元。Desai 感叹,从事硅硬件设计和制造的人是真正的魔法师。相反,GPU 线程寄存器、L1/L2 内存使用 SRAM 进行存储。SRAM 单元由 6 - 晶体管触发器电路组成,并且由于这里涉及到的唯一电容是晶体管栅极,因此访问数据的速度变快了很多。但是,SRAM 也有缺点,它的芯片面积和制造复杂性导致了更高的成本。下面的 SEM 图像为一个 FinFET SRAM 单元。实际上,当有人听到「CUDA 内核」这个词时,并没有任何硬件可以映射成这个人可能想到的东西。CPU 领域的内核要比 FP32 ALU 更加强大,大致对应了英伟达 GPU 的「CUDA 内核」。因此,为了好玩,我们可以试着猜测有多少个晶体管被分配给了一个 RTX 3090 CUDA 内核,它与 AMD Ryzen 7950X CPU 的比较结果见下表。为了记录自己的学习过程,Desai 撰写的博客文章得到了 AI 大牛 Andrej Karpathy 的好评。Karpathy 表示,这篇文章读起来很棒,依据他个人经验,在 AI 研究中,你不仅在与物理规律作斗争,同时也在与 nvidia 编译器和堆栈作斗争,即使在使用了很多技巧之后,我们仍然无法在许多内核上实现超过约 80-90% 的内存带宽,而你原本可能会天真地认为这些内核应该接近 100%,而且这个问题的复杂性非常深。看似一项平常的研究,Desai 却收到了意外惊喜,看来一些有趣而伟大的创造,似乎都在不经意间诞生。机器之心将于2024年6月1日举办「CVPR 2024 线上论文分享会」,将设置 Keynote、 论文分享、企业招聘等环节,就业内关注的 CV 热门主题邀请顶级专家、论文作者与观众做学术交流。© THE END
转载请联系本公众号获得授权
投稿或寻求报道:[email protected]