30 秒看懂
在 Signals and Threads 节目中,Ron Minsky 与 LLVM 和 Swift 的创造者 Chris Lattner 探讨 AI 硬件软件栈的碎片化,以及 Modular 为何创建 Mojo。两人讨论了 GPU 硬件、元编程、类型系统、特化、测试,以及依赖“足够聪明的编译器”所带来的风险。 Lattner 介绍了 Modular 的商业模式,以及他对 Mojo 兼容性、Python 集成和未来发展的计划,同时区分了这些愿景与这场 2025 年对话时语言已经具备的能力。
- 1碎片化的加速器软件栈Lattner 说,硬件厂商围绕自己的设备构建独立软件栈,使提供性能一致且可移植的软件变得困难。
- 2Mojo、MAX 和 Mammoth他介绍了语言层、单节点服务层和集群管理层,并将企业服务作为 Modular 的目标业务。
- 3编译期和运行期使用同一语言Mojo 的元编程旨在让库作者用熟悉的语言结构为硬件特化代码,同时避免运行时解释开销。
- 4超越单一厂商的特化即使尚未考虑跨厂商移植,数据类型、矩阵形状、内存布局和硬件代际也会影响高效内核的设计。
- 5管理抽象的复杂性Lattner 认为,相比不受限制的模板展开,trait 和泛型类型检查可以让接口更容易检查和组合。
- 6当前用途与未来计划他将高性能代码和 Python 扩展列为当前用途,同时把更广泛的语言兼容性视为长期目标。
要点建议
- 针对特定硬件的 AI 软件栈使可移植性与性能难以兼得。
- Mojo 旨在提供类似 Python 的语法,同时让程序员拥有底层控制和编译期元编程能力。
- 了解硬件特性的库可以针对数据类型、布局和设备特化同一算法。
- 相比不透明的编译器优化,可预测的编程模型能让性能工程师拥有更多控制权。
- 经过类型检查的泛型接口可以让抽象更容易推理,但庞大的配置空间仍需要测试。
- Lattner 将更广泛的 Python 兼容性和语言发展视为未来目标,而非已经完成的成果。
按章节阅读整理后的全文
从 LLVM 到 Swift
Lattner 回顾学习计算机、构建 LLVM 和启动 Swift 的经历。他与 Minsky 讨论实际用途和数学基础如何影响语言设计。
Ron Minsky 询问 Chris Lattner 如何进入编译器工程和语言设计领域。Lattner 回忆说,他伴随早期个人电脑长大,从书上逐字输入程序,并学着调试自己犯下的错误。游戏和早期在线讨论激发了他对图形、性能和硬件的兴趣。
在 University of Portland,教授 Steven Vegdahl 对编译器的热情引导 Lattner 进入这一领域。后来他在 University of Illinois 构建了 LLVM。他喜欢编译器课程逐层构建大型系统的方式,而不是只完成互不相干的作业。
Lattner 说,编译器课程也让抽象数据结构在具体系统中发挥了作用。Minsky 将这与操作系统课程相比:学生同样需要逐层构建系统。
Minsky 指出,Lattner 一直在编译器工程和语言设计之间切换。Lattner 说,他的职业经历随兴趣变化:LLVM 让他学习底层代码生成,并为他实现 C++、后来开发更高层的 Swift 语言提供了基础。他认为自己最有价值的工作发生在软硬件交界处。
Lattner 描述了自己从后端代码生成向 Clang 和 Swift 逐步深入的过程。参与 AI 系统和教孩子编程的 iPad 应用,也反映出他希望在技术栈的不同层次解决有用的问题。
Lattner 说,LLVM 始于 2000 年,他于 2005 年加入 Apple。到 2010 年左右,Clang 的 C++ 支持已经能够构建编译器自身及高级库。实现 C++ 在技术上很有趣,但项目交付后他精疲力竭,希望有一门更好的语言。
Swift 最初是他在晚上和周末做的项目,而不是管理层的指令。Lattner 认为,语言设计者不必发明每一种思想:例如模式匹配早已相当成熟,但当时许多主流语言仍没有它。
Minsky 解释了代数数据类型和模式匹配的用处:它们让程序能够表示不同可能性,并可靠地逐一分析。Lattner 说,他以工程师的角度看待这些特性,首先关注它们解决的问题和能够写出的表达力强的代码。
Lattner 说,学习 OCaml 时,模式匹配立刻给他留下了深刻印象:它让普通任务变得简单而富有表达力。他认为,推广特性需要展示实际用途,而形式化类型理论则解释了特性为什么能够正确组合。
Minsky 补充说,扎实的数学基础可以帮助特性推广并组合到最初用途之外。Lattner 同意,好的设计各部分能简单地结合起来,尽管他的设计过程有时会重新发现研究人员早已知道的思想。
Minsky 问 Lattner 在以工程师的方式设计之后,如何寻找数学上的洞见。Lattner 说,他会关注研究,阅读将思想与例子和结果联系起来的解释,尽管密集的数学符号让他不那么自在。
Lattner 说,他经常从论文的例子和结果,或关于学术语言、通俗易懂的博客文章中学习。他重视把复杂特性与具体问题联系起来的解释。Minsky 也认为,非正式解释是严谨理论的补充。
Lattner 把语言复杂性比作一粒沙子,它会在其他地方引发许多麻烦。C++ 面临大量已有约束,不能简单删除旧特性;Swift 和 LLVM 也有他希望重新审视的设计决定。构建新系统是从这些选择中吸取教训的机会。
碎片化的 AI 软件栈
硬件厂商开发了各自的加速器软件栈,支持范围和性能各不相同。Lattner 解释 Modular 为什么希望构建可移植的基础平台和企业服务。
Minsky 问到 Mojo 和 Modular。Lattner 将问题追溯到计算从 CPU 转向 GPU、TPU、FPGA 等 AI 加速器。他说,MLIR 从 LLVM 的经验中吸取教训,为这个更新、更多样的世界提供了基础编译器技术。
Lattner 说,LLVM 为 Swift、Rust 和 Julia 等 CPU 时代的语言提供了共同基础。MLIR 将这些经验应用到更广泛的加速器和 AI 编译问题,但共同的中间层技术并没有消除碎片化。
尽管 MLIR 已被广泛使用,Lattner 说,AI 软件仍分散在 NVIDIA CUDA、Google XLA 和 AMD ROCm 等不同软件栈中。PyTorch 等高层工具必须运行在彼此不兼容的基础上,而硬件和模型也在迅速变化。
他认为碎片化部分源于激励机制。硬件厂商必须为自己的芯片和未来路线图构建软件,很少有资源或动力为竞争对手的硬件建立共同层。相比之下,用户希望软件可以在不同设备间移植。
Minsky 指出,高层框架建立在不兼容的底层系统之上,因此跨平台支持也可能不完整。新硬件和软件快速出现,让协调整个生态更加困难。
Lattner 认为,在不一致的软件栈上再添加一层抽象,不一定能修复性能或可靠性问题。一旦出错,用户仍须调试下面的各层。Modular 希望构建一个更底层、可移植的替代方案,取代针对特定硬件的软件基础。
他说,这个目标刻意定得很难:在厂商自己的硬件上达到或超过厂商软件的性能,尽管 NVIDIA 经验丰富,CUDA 也有多年先发优势。他认为,如果用户希望生态不那么依赖单一厂商,总得有人承担这项工作。
Lattner 将这描述为长期投入,而不是快速修补。他希望可移植的基础平台能帮助更多人使用加速器,并减少对单一硬件供应商的依赖。
Minsky 问,既然销售编程语言出了名地困难,Modular 如何建立共享平台。Lattner 说,公司并不销售 Mojo 本身:语言免费使用,目标业务是在基础平台上提供企业服务。
他描述了管理数百或数千个 GPU 的平台团队,以及不断引入新模型的产品团队。不同硬件、CUDA 版本和快速变化的开源依赖让生产部署变得困难。Modular 希望帮助这些团队解决性能、可观测性、可管理性和跨硬件部署问题。
预期的企业价值不只是更快的内核。Lattner 说,协调设备和软件版本的支持可以减少平台团队必须管理的组合数量,让生产工作负载更容易持续运行。
Lattner 描述了三层架构:Mojo 是编程语言,MAX 是单节点高性能服务层,Mammoth 是集群管理层。他说,自己起初不愿意创建新语言,因为语言开发成本高,也很难说服人们采用。
在他看来,问题是如何编写可移植的加速器代码,同时不放弃昂贵硬件的全部性能。对于团队希望支持的异构设备和特殊数值格式,现有语言没有提供令人满意的答案。
硬件与性能问题
两人比较 CPU 和 GPU 的执行方式、张量核心、硬件代际变化及现有编程工具。Lattner 解释为什么他认为需要一门新语言。
对话转向硬件。CPU 有许多核心,支持成熟的多线程工作负载。GPU 采用不同模型,包含流式多处理器和大量线程;张量核心则为 AI 常见的矩阵乘法提供专用硬件。
Minsky 将 GPU 的 warp 描述为一起执行的线程组,硬件资源使线程切换足够便宜,从而隐藏延迟。张量核心又引入另一种专门的执行模型,其性能和内存特征会随 GPU 代际变化。
Minsky 描述了 NVIDIA 硬件从 Volta 到 Ampere、Hopper 的变化,包括张量核心和内存模型的差异。他和 Lattner 强调,内核必须考虑这些变化,不能假设 GPU 架构始终不变。
Lattner 认为,CUDA 是为更简单的一代 GPU 设计的,难以随着硬件变化而演进。TPU 和其他加速器可能进一步偏离 GPU 的线程模型,围绕大型矩阵运算组织硬件。他提出的 Mojo 目标是从基本原理出发支持这些系统。
他还指出不同硬件代际之间的不兼容,并认为如果工具牺牲过多峰值性能,用户最终可能需要切换到另一套系统。他希望结合易用性、可移植性和足够的控制权,接近硬件全部性能;这些是目标,并非已经普遍得到证明的保证。
Minsky 将 C++ 传统中的底层 GPU 语言与更安全的方案进行比较。未定义行为给编译器优化正确程序的空间,但未初始化变量等错误会让真实程序难以推理和调试。
Lattner 理解 C++ 实现者的处境,因为大量现有代码必须继续运行。优化器往往需要改进不能修改的程序。他认为,在新的 AI 工作负载中,程序员通常能够控制代码,因此更受益于明确的能力和表达力,而非针对特定基准所做的高难度编译器技巧。
Lattner 说,历史遗留约束可以理解,但新语言可以用清晰的编译错误拒绝使用未初始化变量之类的错误。他认为,易用性和诊断信息与原始性能一样,都是 Mojo 设计的一部分。
Minsky 观察到,GPU 内核通常短小却价值很高,编写者也相对少而专业,因此精确控制性能尤其重要。Lattner 补充说,AI 编码工具可以帮助新人学习语言或翻译现有内核,但也提醒这些工具并非魔法。
Mojo 与编译期元编程
Mojo 借鉴 Python 语法,同时追求显式类型、硬件控制和可复用的编译期抽象。Minsky 将它与模板及基于 Python 的 DSL 比较。
谈到 Mojo 的设计,Lattner 说,它从 Python 语法出发,因为 AI 社区很多人已经熟悉 Python。为了性能、明确控制和元编程,Mojo 会在必要时偏离 Python。他希望人们用语言本身构建强大的硬件库,而不是把每一种抽象都硬编码进编译器。
元编程让代码既能在编译期运行,也能在运行期运行。许多语言在两个阶段使用不同机制,例如运行时的普通循环和编译期的递归 C++ 模板。Mojo 希望让程序员在两个阶段使用相似的语言结构。
在编译期使用列表、字符串和算法,让库作者能够在程序运行之前构造更高层的抽象。Lattner 认为,与复杂的模板栈跟踪相比,在不同阶段使用同一语言也有助于调试。
Lattner 说,这对张量核心很重要,因为不同厂商和设备代际的布局与索引方式各不相同。库可以在更高层表示这些细节,并在编译期完成大量工作,而开发者仍使用熟悉的语言和调试工具。
Minsky 把元编程视为解决解释开销的老方法。通用解释器可以表达领域专用计算,但它在运行时做决定。代码生成则可以提前完成已知决策,生成特化的机器代码。
困难在于如何让这些机制容易理解。C 宏和 C++ 模板很强大,但推理起来可能很痛苦,尤其是错误只在深层展开之后才出现时。Minsky 说,Mojo 的目标是用一套语言和类型系统覆盖生成代码与运行时代码,并明确区分阶段。
Minsky 说,他的团队早期使用 Mojo 的体验很有希望。与 C++ 模板和嵌入 Python 的 GPU DSL 相比,他欣赏能够从语法中看出一个值属于编译期还是运行期。这种明确性对性能工程很有用。
Minsky 指出,PyTorch 可以让 Python 代码看起来很直接,实际却通过谨慎延迟操作并重叠 CPU 和 GPU 工作来获得性能。这可能非常高效,但执行模型比源代码看起来更微妙。
两人讨论了“足够聪明的编译器”的吸引力和危险。自动并行化或惰性执行可以让简单示例看起来容易,但很小的代码改动可能使优化失效或性能骤降。发生这种情况时,用户可能不得不检查编译器内部。
可预测性、特化与可移植性
两人讨论不透明编译器优化的局限,以及元编程如何针对类型、形状、布局和硬件特化内核。
Minsky 将利用推测执行和预取来加速顺序程序的 CPU,与更明确并行的加速器进行比较。CPU 灵活,硬件技巧也经常效果显著;明确并行的机器对程序员要求更高,但可能在性能和能效上有优势。
Lattner 说,明确控制是 Mojo 刻意做出的设计选择。在他看来,与全自动接口相比,这会让高级硬件编程更难,却能帮助用户理解并追求所需性能。
Minsky 问元编程如何带来可移植的性能。Lattner 说,即便一个矩阵乘法算法也有许多特化版本:数据类型改变缓存能容纳的数据量,矩阵形状改变访问模式,硬件则规定不同布局和操作。
从 32 位值改为 16 位值,缓存中可容纳的元素数量可能翻倍,因此最佳访问模式也可能改变。矩阵形状又增加了更多情况,所以即使看似简单的通用操作,也可能需要大量特化。
这些差异既存在于不同厂商之间,也存在于同一厂商的产品线内部。Lattner 介绍了一种布局张量抽象,它不仅表示多维数组,还表示内存布局和访问模式,让库能够把计算映射到特定硬件。
他提出的组合是类型系统、编译期元编程,以及了解硬件的领域专家。他希望内核专家构建可复用的库,针对计算问题和目标设备进行特化,而不是把每项优化都嵌入编译器。
Minsky 将好处概括为一个连贯的程序,其中编译期部分会选择并生成针对硬件的代码。接着他提出两个担忧:抽象能否隐藏足够的复杂性,让人们按模块推理;以及如何测试可能极其庞大的配置空间。
类型、泛型代码与可移植包
Minsky 询问如何管理复杂性并测试庞大的配置空间。Lattner 介绍 trait、泛型检查和可移植的包表示。
Lattner 的回答集中在类型和 trait 上,类似 Swift 协议、Rust trait 或 Haskell 类型类。Trait 可以描述不同集合类型上的迭代等操作,让编译器无需实例化每个具体实现,就能根据接口检查泛型代码。
迭代器是共享接口的简单例子:链表、数组和其他来源可以实现同一种概念。泛型代码可以使用该接口,而编译器会检查所需操作是否可用。
Minsky 说,C++ 模板的展开模型在深层组合时可能让错误难以理解,重复生成也会增加编译成本。基于类型的泛型接口可以改善推理和组合。两人的讨论并未给出覆盖所有硬件配置的完整测试策略。
Lattner 说,Mojo 编译器最初解析泛型代码时不会固定目标的指针大小或架构。该表示随后可以针对 CPU 或 GPU 特化。面向 GPU 的部分最终必须打包为适合该设备的独立代码。
Minsky 问,每个可执行文件是否都必须包含面向所有目标的编译器。Lattner 说,普通构建会生成常规可执行文件,而 Mojo 包可以保留可移植性,供之后特化。他将与目标无关的表示这一总体思想与 Java 比较,同时强调两者实现不同。
Minsky 将它与 NVIDIA 的 PTX 相比:PTX 是一种可移植的中间表示,会进一步编译到特定 GPU。Lattner 认为 Mojo 的表示保留了更丰富的泛型类型信息。Minsky 补充说,保留程序员指定的特化方式,让开发者能更好地控制目标代码的生成。
这种区别也关乎控制权。在 Minsky 描述的 PTX 模型中,针对目标的编译大多对程序员隐藏。Mojo 保留泛型表示的意图,则是把特化指令保留到选定目标的时候。
在语言演进中保持简洁
两人考虑语言复杂性、治理和版本兼容性。Lattner 将未来 Mojo 的版本管理与 Python 2 到 Python 3 的迁移进行比较。
Minsky 问,一门语言增加安全和性能所需的精确类型后,如何仍像 Python 一样容易上手。Lattner 说没有完美答案,但 Python 语法给团队提供了起点,也让团队有理由抵制没有充分必要性的特性。
他希望元编程和经过检查的泛型等核心机制能够吸收复杂性,而不是不断累加无关特性。他以 Go 的克制为例:即使每个用户都希望多一个特性,选择较小的特性集合仍能控制复杂度。
Lattner 回忆说,他曾批评 Go 最初没有泛型,但其设计者守住了边界,后来才有意识地加入这一特性。他从中得到的教训是:每个单独看起来有用的特性,都有复杂性成本。
Minsky 将这一挑战称为维持语言的功率重量比。团队需要空间尝试、撤回和调整想法,但一旦庞大的开源生态依赖该语言,这就更难了。Lattner 说,Swift 的开放提案流程带来了有价值的想法,也带来了增加特性的压力。
对于 Mojo,他倾向于由一个小型核心团队审慎决策。他说团队正在讨论 1.0 发布意味着什么,但没有承诺日期;他希望版本机制允许语言变化,同时让不同主版本的包可以互操作。
Lattner 将这一愿景与漫长的 Python 2 到 Python 3 迁移相比:后者要求整个包生态迁移。他指出 C++ 可以链接按不同语言版本构建的代码,希望 Mojo 能将兼容性与帮助迁移源代码的工具结合起来。
编码代理与 Mojo 的未来
对话转向 AI 编码工具、Python 扩展和可能的长期语言特性。Lattner 将愿景与已经得到证明的成果区分开来。
Minsky 认为,强类型系统可以在不执行每条路径的情况下发现错误,从而帮助迁移。他说,代码改动后,编码代理沿着类型错误逐一修复的效果也出奇地好。Lattner 表示赞同,并提到 Rust 稳定 API 的方法中有值得借鉴的思想。
Minsky 问,AI 代理如何使用 Mojo 这样较新的语言,因为模型训练数据中可能没有大量相关内容。Lattner 说,公开的 Mojo 代码和 GPU 内核可以由编码工具索引;正确配置工具以访问仓库会带来很大差别。
对话随后转向 Mojo 与 Python 的关系。Lattner 将对性能敏感的 CPU 和 GPU 代码列为当前用途,并说 Python 扩展支持处于预览阶段。他的目标是让用户把慢的部分移到 Mojo 中,而不必承担通常为另一门语言编写绑定的复杂工作。
在这场对话时,Lattner 将 Python 扩展工作流程描述为预览功能。他说,用户可以保留熟悉的语法,把 Python 包中较慢的部分移到 Mojo 文件,而不一定需要重写整个应用。
他举例说,一些用户获得了显著的 CPU 加速,随后又把工作移到 GPU。这些是他提供的个案,而非通用基准。Minsky 指出,许多 Python 包本来就依赖用 C、C++ 等语言编写的扩展。
Lattner 列出未来可能增加的功能,包括更多泛型能力、运行时 trait 和类似 Python 的类。他设想 Mojo 可用于更广泛的应用编程,并最终更像 Python 的演进版本,同时承认在对话时它还不兼容 Python。
他强调,宏大的愿景尚未得到证明:他对过去的语言工作经验有信心,但希望用户根据当前需求评估 Mojo,例如高性能代码、GPU 内核和 Python 扩展。Minsky 认为,即使改善异构计算这一近期目标,本身也已经很有雄心。
同频道的其他文章
1:48:29EN → 简体中文
1:36:37EN → 简体中文
1:05:45EN → 简体中文
1:25:00EN → 简体中文
1:16:29EN → 简体中文
4:41EN → 简体中文