What Irregularity Costs: CUDA C++, Rust, and Triton on a Hash-Blocked GPU Workload
本文表明,尽管 CUDA C++、Rust 和 Triton 在常规 GPU 工作负载上的表现相似,但在处理由于语言特定在表达原子操作和循环边界方面的局限性而导致的非规则哈希块任务时,它们的效率出现了剧烈分化,其中 Rust 受困于缓存一致性问题,而 Triton 则受限于不可屏蔽的原子操作和编译时循环约束。
原始论文采用 CC BY 4.0 许可(http://creativecommons.org/licenses/by/4.0/)。 这是对下方论文的AI生成解释。它不是由作者撰写或认可的。如需技术准确性,请参阅原始论文。 阅读完整免责声明
舞台与玩家
想象一下,你正试图利用一台拍摄了数千张照片的相机来构建一个房间的 3D 模型。为了实现这一目标,你的计算机需要组织起关于该房间内每一寸微小空间的海量数据。这就是 GPU 编程的世界——其中的“GPU”是电脑内部超快速的图形芯片,它们也非常擅长同时进行数百万次数学计算。
通常,当人们比较用于这些芯片的编程语言时,他们会在非常整洁、可预测的任务上进行测试,比如对巨大的数字矩阵进行乘法运算。这就像是在一条完美笔直、空旷的高速公路上测试赛车。每个车道都是一样的,每个驾驶员都清楚比赛将持续多久。但现实世界是混乱的。在虚拟现实或机器人导航等应用中,计算机必须处理混乱且不可预测的数据。这就像是将同一辆赛车送入一条拥挤、蜿蜒的城市街道,交通拥堵会随机发生,驾驶员必须不断地停下并重新启动。这篇论文提出了一个简单但至关重要的问题:当道路变得混乱时,所有的编程语言表现都一样吗?还是有些语言会陷入交通拥堵,而另一些则能疾速穿行?
伟大的 GPU 语言大对决
在这项研究中,研究人员选取了三种与这些超级芯片沟通的热门方式——CUDA C++(老派的、手写的标准)、Rust(一种以安全性著称的现代语言)以及 Triton(一种旨在简化编码的新型工具)——并将它们投入到一项非常具体的、混乱的任务中:使用“哈希表”(hash table)来构建一个房间的 3D 地图。
把哈希表想象成一个巨大且混乱的储物柜室。你有成千上万的人(数据点)试图寻找一个储物柜(内存中的位置)来存放他们的物品。有时他们想要的储物柜是空的,于是他们就占用了它。但通常情况下,储物柜已经被占用了,所以他们必须检查下一个,再下一个,直到找到一个空位。在一个完美的世界里,每个人都能瞬间找到储物柜。但在这种“不规则”的工作负载中,有些人能立即找到储物柜,而有些人则不得不搜索很长时间,而且所有人都在同时争夺那仅有的几个储物柜。
研究人员在所有三种语言上运行了完全相同的任务,并测量了完成任务所需的时间。结果令人震惊:不同的语言根据任务类型的不同,表现出了截然不同的行为。
“规则”部分:平局
首先,他们测试了这项工作的“规则”部分,这就像是在走廊里走动并粉刷你看到的每一面墙。这部分是可预测的。在这项任务上,三种语言几乎完全相同。无论你使用的是老派的 CUDA、现代的 Rust,还是易于使用的 Triton,它们完成任务的时间都大致相等。如果你只观察这些整洁、可预测的测试(这也是大多数其他研究的做法),你会认为选择哪种语言都无所谓。
“不规则”部分:巨大的分歧
接着,他们测试了“不规则”部分:那个混乱的储物柜搜索过程。这正是故事发生剧烈变化的地方。
- Rust vs. CUDA C++: Rust 语言的表现几乎与手写的 CUDA C++ 一样出色。它仅慢了极小的一点点(在某些情况下约为 1% 到 3%),这实际上可以视为平局。Rust 证明了它处理混乱、不可预测的交通状况的能力可以与这位资深选手媲美。
- Triton 的挣扎: 然而,Tren 却撞上了一堵巨大的墙。在混乱的搜索任务中,它的速度比其他两种语言慢了 10 倍以上。在一些涉及实际房间扫描的真实世界测试中,它甚至比其他两者慢了近 30 倍。
为什么 Triton 会被困住?
研究人员并没有仅仅说“Triton 很慢”;他们弄清楚了 Triton 被困住的究竟是为什么,而这并不是因为代码写得不好,而是因为该语言本身的构建方式。
想象一下,Triton 是一位严格的老师,他坚持要求班上的每个学生即使提前完成了工作,也必须在座位上坐满固定的时间。在混乱的储物柜室里,有些线程(学生)在一秒钟内就找到了储物柜,而另一些则需要花费十秒钟。
- 问题所在: Triton 强制要求快速线程在慢速线程完成之前一直处于循环等待状态,即使它们已经无事可做。这就像一场比赛,获胜者必须站在原地,等待最后一个人跨过终点线后,所有人才能离开赛道。
- “掩码”(Mask)问题: 此外,Triton 缺乏一种特定的工具(称为“掩码”),可以让快速线程完全停止工作。相反,它们必须继续执行一个虚假的(dummy)任务,从而浪费能量并阻塞系统。研究人员发现,这种设计选择迫使计算机做了大量的无用功,导致整体速度下降了 10 到 30 倍。
- 隐藏的危险: 还存在一个安全问题。由于 Triton 对搜索设置了固定时间限制,如果储物柜室变得过于拥挤,搜索可能会放弃并停止寻找。这意味着计算机会在不知不觉中丢弃 3D 房间的部分内容,从而在最终模型中产生肉眼看不见的空洞。研究人员发现,在某些拥挤水平下,Triton 会丢失整个表面的数据,而其他语言则能完美地找到它们。
为什么 Rust 稍慢了一些(隐形的陷阱)
Rust 非常接近胜利,但它并没有完全赶上手写的 CUDA 代码。研究人员花费了大量时间试图找出原因,检查了指令数量和使用的内存。他们发现,Rust 实际做的功比 CUDA 还少,但它仍然更慢。
罪魁祸首是 Rust 处理安全性时的一个隐藏陷阱。Rust 有一个功能,通过确保每个人看到的版本都一致,使读取共享数据变得“安全”。然而,在这些特定的芯片上,这种“安全”的读取方式会迫使计算机跳过其最快的内存缓存,转而使用较慢的缓存。这就像一名保安坚持要检查门口的每一个包裹,即便这些包裹早已被确认是安全的。这个额外的步骤让 Rust 慢了大约 20-30%,但与 Triton 的大规模减速相比,这只是一个微小的代价。
总结
这篇论文的主要教训是:你不能仅仅通过观察一种语言在整洁、可预测任务上的表现,来判断它的优劣。
如果你只在“规则”的高速公路上进行测试,Rust、CUDA 和 Triton 看上去都是冠军。但一旦你把它们投入到现实世界 3D 建模中那种“不规则”的城市交通中,结果就会出现巨大的分歧。
- Rust 是一个强有力的竞争者,它能保持与顶尖手写代码几乎相当的速度。
- Triton 虽然擅长整洁的任务,但在处理混乱、不可预测的工作时却表现挣扎,速度会变慢数十倍,并且可能在无人察觉的情况下丢失数据。
研究人员得出结论:对于涉及混乱、现实世界数据的任务,选择错误的语言不仅仅是一个小小的麻烦,它可能会让你的程序变得极其缓慢,或者导致其在无声无息中失败。他们还发现,由于许多先前的研究只测试了“规则”的部分,导致那些危险且混乱的部分从未被探索过。
您所在领域的论文太多了?
获取与您研究关键词匹配的最新论文每日摘要——附技术摘要,使用您的语言。