上周 Mojo 发 1.0(带 source stability 承诺),这周 Modular 把整个编译器、工具链,以及所有构建这门语言需要的东西都开源了,许可证是 Apache 2.0(附 LLVM exceptions),代码进 modular 的 GitHub 仓库。发布节奏值得注意:先承诺源码稳定,再开编译器。这个顺序跟很多项目反着来。
我把这条消息放在技术博客里而不是新闻栏里,是因为它对"编译器怎么写"这件事有参考价值,而不只是一条发布公告。
Mojo 想解决什么
Mojo 的定位不太好用一句话概括。它表面上像 Python——def、缩进、动态风格都能写;但它的目标是编译到 CPU、GPU 和 NPU 上,并且要能写出接近手写 CUDA 的性能。
这个定位的关键在于"一套语法,多个后端"。Python 做不到这一点,因为它是解释执行加 C 扩展的混合体,性能靠外挂;CUDA 只能跑 GPU,写不了通用逻辑。Mojo 试图同时拿下"写起来像脚本"和"跑起来像编译语言",而它的实现地基不是传统编译器,是 MLIR。
MLIR 的分层:为什么不是直接写 LLVM IR
传统编译器前端直接吐 LLVM IR,问题是从 Python 那样的高层语义到 LLVM IR 之间的落差太大,中间没有地方表达"这是矩阵乘""这是 SIMD 向量化前的中间态"。优化 pass 只能在一堆已经降格成标量指令的 IR 上做,很多结构信息已经丢了。
Mojo 走的是 MLIR 路线,也就是方言(dialect)分层。高层是 Mojo 自己的方言,保留所有权、值语义、参数化这些语言级概念;往下逐步降格经过若干中间方言,把并行结构、循环嵌套、内存布局显式化;最后才降到 LLVM IR,或者对 GPU 直接降到 NVPTX、AMDGCN 这类目标方言。
这样做的好处是每一层的优化都有对应的表达力。比如循环融合、tiling、向量化这些针对数值计算的变换,可以在保留"这是个嵌套循环"这一信息的方言上做,而不是等到已经拆成寄存器级操作之后再去猜。
语言层面的几个决策
开源之后可以直接看实现,但有些设计从文档就能读出来,对写代码的人更直接:
参数所有权用 owned、borrowed、inout 三档。跟 Rust 的 move/borrow 思路相近,但表达更显式,目的同样是让编译器能在不插入额外交互的前提下决定数据怎么传。
fn 和 def 是两种函数:def 是动态的、宽松的,写起来像 Python;fn 是静态的、强类型的、默认禁用隐式动态行为。这给了同一种语言两个档位——原型用 def 快速写,性能敏感的路径改成 fn 让编译器放开优化。这个设计比"全静态"友好,比"全动态"可控。
值语义和内存布局可以手动控制。@value 装饰器让结构体具备值语义,@register_passable 允许类型直接走寄存器而不是内存。这两个特性是"写出接近硬件性能的代码"的前提——如果结构体的布局由运行时决定,你就没法规划 SIMD 对齐和访存模式。
编译期元编程靠参数化实现,不是模板字符串展开。类型和值都可以当参数传,编译器在实例化时做特化。这让"为每种数据类型生成一份优化过的实现"变得自然,而不是靠宏系统拼代码。
Apache 2.0 加 LLVM exceptions 意味着什么
许可证的选择比语言特性更能说明项目的野心。
Apache 2.0 是编程语言和编译器生态里的默认选择,因为它允许商业使用、修改、闭源分发,还带了明确的专利授权条款。加"LLVM exceptions"是为了能跟 LLVM 生态(本身是 Apache 2.0 with LLVM exceptions)互操作,不会因为许可证边界产生摩擦。
对照一下:Python 用 PSF 许可,Rust 用 MIT/Apache 双许可。Mojo 选择跟 LLVM 一致的许可,等于明确表态它想成为 LLVM 生态的一部分,而不是另起炉灶。对使用者来说,这意味着编译出来的产物可以自由分发,企业采用的法务阻力小。
现实的约束
开源编译器不等于生态就绪。一个语言的价值大部分在库和工具链,不在语法。Python 的库生态是三十年积累出来的,Mojo 现在有的是编译器加一套标准库,离"能拿来干日常活"还有距离。
Modular 自身的商业模式也决定了开源边界。他们把编译器开源,但 MAX 平台(面向模型部署的商业产品)是另一回事。这个分法很正常,跟 Red Hat 卖支持、HashiCorp 卖企业版是同一类打法。作为使用者,你要判断的是:如果商业公司哪天调整策略,你依赖的部分会不会被卡住。
从 1.0 的 source stability 承诺来看,Modular 至少意识到了语言项目最怕的是"每升一版就要改代码"。先稳语言再开源编译器,是为了避免早期采用者被反复的 breaking change 劝退。这个判断是对的——编译器开源之后,社区会开始写扩展和工具,这时候语言层再乱动,生态就散了。
我的做法
什么时候值得上手 Mojo,我给一个直接的判断标准:
- 如果你的代码里有大量数值 kernel,尤其是要同时跑到 CPU 和 GPU 上的,Mojo 的"一套语法多后端"设计能省掉维护两套实现的成本,值得试。装
pip install mojo就能跑,前面的学习曲线主要在所有权和参数化这两块。 - 如果你做的是 Web 后端、CLI 工具、脚本自动化,暂时不必。这些场景 Python 的生态优势压倒一切,Mojo 的性能优势用不上,反而要承担生态不成熟的代价。
编译器开源还有一个间接好处:以前遇到"这个优化为什么没生效",只能看文档;现在可以直接翻 pass 的实现,甚至自己改。对关心编译器内部机制的人来说,这才是开源最实在的部分。
浙公网安备 33010602011771号