Mojo是一种新的编程语言,通过将Python语法的优势与系统编程和元编程相结合,弥补了研究和生产之间的差距。使用Mojo,您可以编写比C更快的可移植代码,并与Python生态系统无缝互操作。
Mojo的意义🔥
一个关于我们创造Mojo语言的背景故事和理由。
当我们开始创建Modular时,我们并没有打算构建一种新的编程语言。但是,当我们正在构建我们的统一全球机器学习/人工智能基础架构的平台时,我们意识到整个堆栈上的编程过于复杂。此外,我们手写了很多MLIR代码,但并不开心。
我们想要的是一种创新且可扩展的编程模型,可以针对加速器和在AI领域普遍存在的其他异构系统。这意味着需要一种编程语言,具有强大的编译时元编程、自适应编译技术的集成、编译流程中的缓存以及其他现有语言不支持的功能。
虽然加速器很重要,但最常见且有时被忽视的“加速器”之一是主机CPU。如今,CPU具有许多类似张量核心的加速器块和其他AI加速单元,但它们还充当了专用加速器无法处理的操作的“回退”,例如数据加载、前后处理和与外部系统的集成。因此,很明显我们不能仅通过一个仅适用于特定处理器的“加速器语言”来推动AI。
应用AI系统需要解决所有这些问题,我们决定用同一种语言实现这一目标。因此,Mojo诞生了。
下一代编译器技术的语言
当我们意识到没有任何现有语言可以解决AI计算中的挑战时,我们开始从首要原则重新考虑如何设计和实现解决我们问题的编程语言。由于我们需要对各种加速器提供高性能支持,传统的编译器技术如LLVM和GCC并不适合(任何基于它们的语言和工具也无法满足我们的需求)。尽管它们支持多种CPU和一些常用的GPU,但这些编译器技术是几十年前设计的,无法完全支持现代芯片架构。如今,专用机器学习加速器的标准技术是MLIR。
MLIR是一个相对较新的开源编译器基础架构,最初由Google(其主要负责人转到Modular)启动,已在机器学习加速器社区广泛应用。 MLIR的优势在于它能够构建领域特定编译器,特别是用于不是传统CPU和GPU的奇怪领域,例如AI ASIC、量子计算系统、FPGA和定制硅。
鉴于我们在Modular中构建下一代AI平台的目标,我们已经在一些基础设施中使用了MLIR,但我们没有一种编程语言可以完全发挥MLIR在整个堆栈上的潜力。虽然现在许多其他项目都在使用MLIR,但Mojo是第一个专为MLIR而设计的主要语言,这使得Mojo在编写面向AI工作负载的系统级代码时具有独特的强大能力。
Python语言家族的一员
Mojo的核心使命包括创新编译器内部和对当前和新兴加速器的支持,但我们并不认为有必要在语法或社区方面创新。因此,我们选择拥抱Python生态系统,因为它被广泛使用,被人工智能生态系统所喜爱,并且我们认为它是一种非常好的语言。
Mojo语言有很高的目标:我们希望与Python生态系统完全兼容,我们希望能够实现可预测的低级性能和低级控制,并且我们需要能够将代码子集部署到加速器上。此外,我们不希望创建一个分散的软件生态系统 - 我们不希望采用Mojo的Python用户将其与从Python 2迁移到Python 3的痛苦经历相比较。这些目标并不轻松!
幸运的是,尽管Mojo是一个全新的代码库,但在概念上我们并不是从零开始。拥抱Python极大地简化了我们的设计工作,因为大部分语法已经被指定了。我们可以将精力集中在构建Mojo的编译模型和系统编程特性上。我们还从其他语言(例如Rust、Swift、Julia、Zig、Nim等)和我们以前将开发人员迁移到新编译器和语言的经验中学到了很多宝贵的经验,并利用了现有的MLIR编译器生态系统。
此外,我们决定Mojo的正确长期目标是提供Python的超集(即使Mojo与现有的Python程序兼容),并拥抱CPython实现以支持长尾生态系统。如果你是Python程序员,我们希望Mojo对你来说可以立即熟悉,并提供新的工具来开发安全和高性能的系统级代码,而这些代码在Python下通常需要用C和C++来完成。
我们不试图说服世界“静态是最好的”或“动态是最好的”。相反,我们认为当它们用于正确的应用时,两者都是好的,因此我们设计Mojo,以便您作为程序员可以自行决定何时使用静态或动态。
我们为什么选择Python
Python在机器学习和无数其他领域中是占主导地位的。它易于学习,被重要的程序员群体所熟知,拥有一个令人惊叹的社区,拥有大量有价值的软件包,并且拥有各种各样的良好工具。通过其动态编程特性,Python支持开发精美而富有表现力的API,这使得诸如TensorFlow和PyTorch等机器学习框架将Python作为其用于高性能运行时的前端。
对于Modular来说,Python是我们API面向栈的不可协商的一部分 - 这是由我们的客户决定的。考虑到我们栈中的其他所有内容都是可以商讨的,所以从“首选Python”出发是合理的。
更主观地说,我们认为Python是一种美丽的语言。它采用简单和组合的抽象,避免不必要的标点符号,这些标点符号在实践中与缩进重复,它还具有强大的(动态的)元编程功能。所有这些为我们扩展语言到Modular所需的方向提供了一个途径。我们希望Python生态系统中的人们将我们对Mojo的方向视为将Python推向更高水平的完善,而不是将其与之竞争。
与Python的兼容性
我们计划与Python生态系统实现全面兼容,但实际上有两种类型的兼容性,以下是我们目前对它们的情况说明:
- 在能够导入现有Python模块并在Mojo程序中使用它们方面,Mojo是100%兼容的,因为我们使用CPython实现互操作性。
- 在能够将任何Python代码迁移到Mojo方面,目前尚不完全兼容。Mojo已经支持了许多Python的核心功能,包括异步/等待、错误处理、可变参数等等。然而,Mojo仍然年轻,还缺少Python的许多其他功能。甚至Mojo目前还不支持类!
还有很多工作要做,但我们相信我们会实现目标,并且我们的团队在构建其他重要技术及其兼容性过程中具有丰富经验,这些经验可以指导我们:
- Clang编译器(用于C、C++、Objective-C、CUDA、OpenCL等的编译器)的过程,它是GCC、MSVC和其他现有编译器的“兼容替代品”。这很难进行直接比较,但是Clang问题的复杂性似乎比实现Python的兼容替代品要大一个数量级。
- Swift编程语言的过程,它采用了Objective-C运行时和语言生态系统,逐步迁移了数百万程序员(以及大量的代码)。通过Swift,我们学到了如何实现“运行时兼容性”并与传统运行时合作的经验。
在需要混合使用Python和Mojo代码的情况下,我们预计Mojo将直接与CPython运行时进行合作,并且具有与CPython类和对象集成的类似支持,而无需编译代码本身。这为现有代码生态系统提供了插件兼容性,并且支持逐步迁移方法,即逐步迁移到Mojo可以产生逐步的好处。
总的来说,我们相信通过专注于语言设计和逐步实现与Python的兼容性,我们将在适当的时间达到我们的目标。
然而,重要的是要理解,当您编写纯Mojo代码时,实现、编译或运行时没有使用任何现有Python技术。Mojo本身是一种全新的编程语言,具有全新的编译和运行时系统。
与Python的区别
虽然Python的兼容性和可迁移性对Mojo的成功至关重要,但我们也希望Mojo成为一门一流的语言(意味着它是一种独立的语言,而不是依赖于其他语言)。它不应受到限制,以维护兼容性而仅仅引入新关键字或语法结构。因此,我们对兼容性的方法具有双重目标:
- 我们利用CPython运行所有现有的Python 3代码,无需修改,并使用其运行时,以实现与整个生态系统的完全兼容性。以这种方式运行代码对Mojo没有任何好处,但是 MoJo的存在和可用性将迅速加速Mojo的推广,并利用了Python已经非常适合高级编程的事实。
- 我们将提供一个机械迁移工具,用于提供对希望从Python迁移到Mojo的人非常好的兼容性。例如,为了避免迁移Python代码时出现使用与Mojo关键字匹配的标识符名称的错误,Mojo提供了一个反引号功能,允许任何关键字作为标识符使用。
通过这两点,Mojo可以在主要基于CPython的环境中很好地集成,但允许Mojo程序员逐步将代码(一个模块或一个文件一次)迁移到Mojo。这是Objective-C向Swift迁移的经过验证的方法,也是Apple采用的方法。
构建其余的Mojo和迁移支持需要一些时间,但我们确信这种策略可以让我们集中精力并避免分散注意力。我们也认为与CPython的关系可以双向发展——如果CPython团队最终在Mojo而不是C中重新实现解释器,那将是多么酷炫!🔥
Python的问题
通过将Mojo设计为Python的超集,我们相信我们可以解决Python现有的许多问题。
Python有一些众所周知的问题,最明显的是低级性能较差和CPython实现细节,如全局解释器锁(GIL),导致Python是单线程的。虽然有许多正在进行中的项目来改善这些挑战,但Python带来的问题更深层次,并且在人工智能领域尤其有影响力。我们不打算详细讨论这些技术限制,而是在这里谈谈它们在2023年的影响。
请注意,在本节中提到的Python处处指的是CPython实现。我们将在后面讨论其他实现。
两界问题
出于各种原因,Python并不适合用于系统编程。幸运的是,Python作为黏合层具有强大的优势,通过与C和C++的低级绑定可以构建使用C、C++和许多其他具有更好性能特性的语言编写的库。这正是使NumPy、TensorFlow、PyTorch和众多其他库在生态系统中得以实现的原因。
然而,尽管这种方法是构建高性能Python库的有效途径,但也有一个代价:构建这些混合库非常复杂。它需要对CPython内部的底层机制有深入的了解,需要对C/C++(或其他)编程有一定的知识(从根源上破坏了最初使用Python的目标之一),使得大型框架的进化变得困难,还(在机器学习领域)将世界推向了“基于图形”的编程模型,其基本可用性不如“即时模式”的系统。TensorFlow和PyTorch在这方面都面临着重大挑战。
除了两界问题造成系统复杂性的基本性质之外,它还使生态系统中的其他所有内容变得更加复杂。调试器通常无法跨越Python和C代码进行调试,而且那些能够跨越调试的调试器也没有得到广泛接受。让Python软件包生态系统不仅要处理Python代码,还要处理C/C++代码,这是痛苦的。像PyTorch这样在C++上进行了重大投资的项目,故意试图将更多的代码转移到Python上,因为他们知道这样可以提高可用性。
三界和N界问题
两界问题普遍存在于Python生态系统中,但对于机器学习框架的开发人员来说,情况更加糟糕。人工智能广泛采用加速技术,而这些加速器使用专门的编程语言,如CUDA。虽然CUDA与C++有关联,但它有自己的特殊问题和限制,并且没有像调试器或性能分析器这样一致的工具。它也只能在特定硬件制造商的产品上使用。
人工智能领域在硬件方面有着令人难以置信的创新,结果复杂性失控。现在有几种尝试为加速器构建有限的编程系统(OpenCL、Sycl、OneAPI等)。这种复杂性爆炸不断增加,而且没有一个系统可以解决工具和生态系统中的根本分裂问题,这对行业造成了严重伤害——它们只会“增加分裂”。
移动和服务器部署
Python生态系统面临的另一个挑战是部署。这涉及许多方面,包括如何控制依赖关系,如何部署经过编译的“a.out”文件,并提高多线程和性能。我们希望看到Python生态系统在这些领域迈出重要的一步。
相关工作
我们知道有许多其他改进Python的努力,但它们并不能解决我们在Mojo中致力于解决的基本问题。
一些正在进行的改进Python的努力包括提高Python性能和替换全局解释锁(GIL),构建类似于Python但是Python子集的语言,以及构建嵌入领域专用语言(DSL),这些语言与Python集成,但并非第一类语言。虽然我们无法提供所有努力的详尽列表,但我们可以谈谈这些项目面临的一些挑战,以及它们为什么不能解决Mojo解决的问题。
改进CPython和即时编译Python
最近,社区投入了大量精力改进CPython性能和其他实现问题,并取得了巨大成果。这项工作非常棒,因为它逐步改进了当前的CPython实现。例如,Python 3.11通过内部改进使性能比Python 3.10提高了10-60%,而Python 3.12目标是通过跟踪优化器进一步提高性能。许多其他项目尝试解决GIL问题,而PyPy等项目则使用即时编译和跟踪方法加速Python。
虽然我们支持这些伟大的努力,并认为它们对社区非常有价值和令人兴奋,但遗憾的是它们不能满足Modular的需求,因为它们不能提供针对加速器的统一语言。现在的许多加速器仅支持非常有限的动态特性,或者以极低的性能支持动态特性。此外,系统程序员不只是追求“性能”,他们通常还希望在计算过程中有很多可预测性和控制性。
我们希望消除在Python库中使用C或C++的需要,追求最高的性能,并且在某些情况下不能接受动态特性。因此,这些方法对我们没有帮助。
Python的子集和其他类似Python的语言
目前有许多尝试构建“可部署”Python的项目,例如来自PyTorch项目的TorchScript。这些项目非常有用,因为它们通常提供了低依赖性的部署解决方案,并且有时具有很高的性能。由于这些项目使用了类似Python的语法,因此学习起来可能更容易,而不是学习一门新的语言。
另一方面,这些语言并没有得到广泛的应用——因为它们是Python的一个子集,通常无法与Python生态系统进行互操作,缺乏出色的工具(例如调试器),并且经常单方面更改Python中的不便行为,这破坏了兼容性并进一步使生态系统分裂。例如,其中许多将简单整数的行为更改为循环而不是生成兼容Python的数学结果。
这些方法的挑战在于它们试图解决Python的一个弱点,但在Python的优点方面并不出色。充其量,这些方法可以提供一个与C和C++有些不同的选择,但是由于不能解决Python的动态使用案例,“两个世界”问题依然无法解决。这种方法导致了碎片化,并且不兼容性使得迁移变得困难甚至不可能——还记得从Python 2迁移到Python 3是多么具有挑战性吗?
具有C兼容性的Python超集
因为Mojo被设计为是Python的超集,具有改进的系统程序设计能力,因此它与其他Python超集(如Pyrex和Cython)共享一些高级思想。与Mojo一样,这些项目定义了自己的语言,也支持Python语言。它们允许您编写更高效的Python扩展,可以与Python和C库进行互操作。
这些Python超集对于某些类型的应用程序非常有用,而且一些流行的Python库已经成功应用了它们。然而,它们没有解决Python的两个世界问题,因为它们依赖于CPython来实现核心语义,所以无法独立工作, 而Mojo仅在必要时使用CPython来提供与现有Python代码的兼容性。纯粹的Mojo代码不使用任何现有的运行时或编译器技术,而是使用基于MLIR的基础架构在各种硬件上实现高性能执行。
Python中的嵌入式领域特定语言(DSL)
另一种常见的方法是在Python中构建嵌入式领域特定语言(DSL),通常使用Python装饰器进行安装。有许多示例(例如TensorFlow中的@tf.function
装饰器,OpenAI的Triton编程模型中的@triton.jit
,等等)。这些系统的一个主要优点是它们与Python生态系统的工具保持兼容,并在Python逻辑中本地集成,允许嵌入式迷你语言与Python的动态使用案例共存。
不幸的是,由这些系统提供的嵌入式迷你语言通常具有令人惊讶的限制,无法很好地与调试器和其他工作流工具集成,并且不支持我们寻求的一种语言的本地集成水平,该语言统一异构计算,是编写大规模内核和系统的主要方式。
通过Mojo,我们希望通过简化和提高一致性来推动整个系统的易用性。嵌入式领域特定语言是一种快速启动和运行演示的便捷方式,但是我们愿意付出额外的努力和工作,以提供更好的可用性和可预测性,以满足我们的使用案例。