放弃Python拥抱Mojo?鹅厂工程师真实使用感受
# 关注并星标腾讯云开发者
(资料图片)
# 每周1 | 鹅厂工程师带你审判技术
#第1期|李志瑞:AI 届新语言 Mojo 要火??
前段时间 Modular 发布了一个新语言 Mojo,这语言不止官网放了巨大的 emoji ?,而且它的标准文件后缀一个是「.mojo」另一个是「.?」,一副立马要火的样子呢。
说实话,这个用 emoji 做后缀名的操作其实挺无聊,也有点败好感,但如果说这个语言能在完全兼容 Python 的基础上大幅提高执行效率,并且作者是 LLVM 发起人 Chris Lattner,是不是突然又有兴趣继续了解它了呢?
Mojo 被设计为 Python 语言的超集,并增加了许多特性,包括:
▶︎ Progressive types:能利用类型信息获得更好性能和静态检查,但又不强制要求写类型。
▶︎ Zero cost abstractions:C++ 的核心设计准则,能够避免用户为了性能放弃合理设计的代码。
▶︎ Ownership + borrow checker:Rust 语言的安全性来源,在编译期避免许多错误的发生。
▶︎ The full power of MLIR:原生支持对 MLIR 的直接访问,能够从底层扩展系统。
在 Mojo 这个语言的介绍中反复提到 AI,官网也说它是「a new programming language for all AI developers」。那么为什么 AI 开发需要一个新语言呢?首先,我们知道在 AI 届具有统治地位的语言就是 Python,Python 是一个语法简单清晰,容易上手,且灵活度很高的语言,深受广大程序员喜爱,XKCD 上有就这么一幅漫画:
当然,受人喜爱的语言有很多,Python 成为 AI 届的统治语言除了本身易用之外,也有惯性的因素。由于 Python 上机器学习相关的库多,因此机器学习从业者用的就多,这又反过来令新的机器学习相关库优先为 Python 提供接口,进一步加强了其统治地位。因此,为了逐步渗透这个用户群,Mojo 兼容 Python 是很正确的一个选择。Mojo 不仅承诺语法是 Python 的超集,并且它还能直接调用 Python 的库,这意味着 Mojo 不需要从零开始构建自己的生态,本身就可以用上繁荣的 Python 生态了。
虽然 Python 很好,但它有一个众所周知的问题,那就是太慢了。而机器学习本身又需要繁重的计算,因此 Python 生态中大量库的底层其实都是用高性能的语言(如 C/C++)进行实现,然后再提供一个 Python 接口供用户调用,典型的如 numpy 这种数学库。在这种情况下,Python 事实上是被作为一个胶水语言来使用,这造成了开发的碎片化,如果一个用户只是简单调一下库那还好说,但一旦到了工业界,开发过程中不可避免地就要涉及一些底层库的修改,甚至直接换语言来实现同样的功能以提高性能,这种割裂不止增加了开发成本和精神负担,而且考虑到众多擅长 C/C++ 语言的开发者也并不是 AI 领域专家,这种开发人员能力的不适配也对整个 AI 生态的发展形成了一定阻碍。
因此,Mojo 的目的就是要在 Python 生态的基础上,让用户能用一个语言,从使用易用的接口,到开发复杂的库,再到实现底层黑科技,统一实验和生产环境所用的语言。为了实现这个目的,Mojo 扩展了 Python 语法,支持了紧凑的内存布局,并引入了一些现代的语言特性(例如 Rust 的安全性检查),使得这个语言能够渐进式地在 AI 界立足。说起来 Chris Lattner 在这方面可以算是经验丰富了,不管是在 gcc/msvc 的统治下实现 clang,还是在 objective-c 的统治下为苹果实现 swift,都是一个逐步蚕食对手市场的过程。
说了这么多,该来看看 Mojo 长什么样了。现在 Mojo 还不能直接下载使用,如果想要尝鲜,需要在官网申请,然后在 playground 页面中试用,这是一个基于 Jupyter 的页面,可以混合笔记和可执行的 Mojo 代码。
前面提到,Mojo 的语法是 Python 的超集,因此 Mojo 的 Hello World 也跟 Python 一样简单:
print("Hello World") #> Hello World
与 Python 一样,Mojo 也使用换行符和缩进来定义代码块:
fn foo():var x: Int = 1x += 1let y: Int = 1print(x, y) #> 2 1foo()
上面的代码中使用 var 来声明变量 x,使用 let 来声明了不可变量 y。Mojo 像很多较新近的语言一样,让不可变量的声明变得简单,以鼓励开发者使用不可变的量。另外注意到这里定义函数使用了 fn 而非 Python 的 def,这是因为 Mojo 希望在兼容 Python 的基础上加入编译期的检查和优化,而 Python 过于动态的语法很难支持这一目标,因此,Mojo 同时支持使用 fn 和 def 两个关键字来声明函数,对于调用者来说,这两种方法声明出来的函数没有什么区别,但对于实现者来说,可以将 fn 看作「严格模式」下的 def,例如下面的代码会编译错误(如果改成用 def 则不会出错):
fn foo(): x = 1 print(x) #error:Expression[12]:6:5:useofunknowndeclaration"x","fn"declarationsrequireexplicitvariabledeclarations#x=1#^
虽然官方承诺 Mojo 的语法是 Python 的超集,但目前 Mojo 还在开发中,很多 Python 语法都还不支持,例如目前连 Python 的 class 都无法被编译通过:
class MyClass:def foo():pass# error: Expression [15]:17:5: classes are not supported yet# class MyClass:# ^
不过,Mojo 现在先提供了另一个用来组织数据的关键字 struct,相比于 class,struct 更加静态可控,便于优化。一方面,struct 支持类似 Python class 风格的函数声明和运算符重载。而另一方面,struct 又类似于 C++ 的 struct 和 class,内部的成员在内存中紧凑排布,而且不支持在运行时动态添加成员和方法,便于编译期进行优化,例如:
struct MyIntPair:var first: Intvar second: Intfn __init__(inout self, first: Int, second: Int):self.first = firstself.second = secondfn __lt__(self, rhs: MyIntPair) -> Bool:return self.first < rhs.first or(self.first == rhs.first andself.second < rhs.second)let p1 = MyIntPair(1, 2)let p2 = MyIntPair(2, 1)if p1 < p2: print("p1 < p2") #> p1 < p2
虽然有点不同,但整体上看起来还是非常熟悉的对吧。说到这里,有一点需要提醒各位注意,尽管 Mojo 之后会令语法成为 Python 语法的超集,但其语义则有时会和 Python 不同,这意味着 Python 的代码直接拷到 Mojo 里可能会出现编译通过但执行结果不同的情况,这里简单提一个比较常见的例子:函数传参。在 Python 中,函数传参的语义类似于 C++ 的传指针,在函数内部虽然不能更改调用者指向的对象,但可以改变该对象内部的状态,例如下面的代码:
def foo(lst):lst[0] = 5print(lst)x = [1, 2, 3]foo(x)print(x)
在 Python 中,这段代码打印出来的结果是两次 [5, 2, 3]。但在 Mojo 中,使用 def 定义的函数默认的传递逻辑是复制值,也就是说,尽管在函数中能够修改参数内部的状态,但修改对于调用方来说是不可见的,因此上面这段代码在 Mojo 中打印的结果是 [5, 2, 3](foo 内部)和 [1, 2, 3](foo 外部)。
除了语法像 Python,Mojo 非常务实的一点在于它构建于 Python 的生态之上。因此即便 Mojo 还没能完整支持 Python 的语法,它还是优先支持了对 Python 库的调用,以便让开发者能受益于庞大完善的 Python 的生态。例如下面的代码就使用了 Python 的 numpy 库:
fromPythonInterfaceimportPythonlet np = Python.import_module("numpy")ar = np.arange(15).reshape(3, 5)print(ar.shape) #> (3, 5)
Mojo 作为一个新语言,广泛吸收许多现代的程序语言设计思想,例如 Rust 的所有权和借用检查,以此提升代码的安全性。在 Mojo 中,使用 fn 定义的函数的参数默认传的是不可变的引用,即「借用」,调用方仍然拥有其所有权,因此在函数内部不可以对参数进行修改。Mojo 提供了一个 borrow 关键字来标注这样的参数传递情况,对于 fn 来说是可以省略的,也就是说下面 foo 函数中两个参数的传递方式相同:
fn foo(borrowed a: SomethingBig, b: SomethingBig):a.use()b.use()
在 Rust 中,传参的默认行为是移动,如果需要借用则需要在传入时加上 &,这两种方式倒是没有太大的优劣之分,Mojo 的行为可能更接近于 Python 这类高级语言的习惯。如果想要修改传入的参数,则需要手动注明 inout,例如:
fn swap(inout lhs: Int, inout rhs: Int):let tmp = lhslhs = rhsrhs = tmpfn test_swap():var x = 42var y = 12print(x, y) #> 42, 12swap(x, y)print(x, y) #> 12, 42test_swap()
按道理说,Mojo 应该像 Rust 一样规避一个变量同时被可变和不可变借用,也应该规避同时被可变借用,但目前 Mojo 编译器似乎还没实现这一特性,例如下面的代码还是能编译通过的:
var x = 42swap(x,x)
从这也可以看出 Mojo 确实还处在比较早期的发展阶段。
另一个重要的内存安全概念是对象的所有权,当一个函数获取了对象的所有权后,调用方就不应该再去使用这个对象了,例如我们实现了一个只支持移动的类型 UniquePtr:
struct UniquePtr:var ptr: Intfn __init__(inout self, ptr: Int):self.ptr = ptrfn __moveinit__(inout self, owned existing: Self):self.ptr = existing.ptrfn __del__(owned self):self.ptr = 0
同时,我们有两个函数,其中,use_ptr 使用了前面提到的 borrow 关键字,借用了 UniquePtr 对象,而 take_ptr 则使用 owned 关键字,指明它需要获取传入对象的所有权。那么,在调用 take_ptr 的时候,我们就需要在参数后面加上 ^ 后缀,用来表明我们将所有权转移给 take_ptr:
fn use_ptr(borrowed p: UniquePtr):print(p.ptr)fn take_ptr(owned p: UniquePtr):print(p.ptr)fn test_ownership():let p = UniquePtr(100)use_ptr(p) #> 100take_ptr(p^) #> 100test_ownership()
因此,如果我们将 use_ptr 和 take_ptr 的调用顺序调换一下,就会出现编译错误:
fn test_ownership():let p = UniquePtr(100)take_ptr(p^)use_ptr(p) # ERROR!test_ownership()# error: Expression [13]:23:12: use of uninitialized value "p"# use_ptr(p) # ERROR: p is no longer valid here!# ^
Mojo 的另一个强大之处在于它让对 MLIR>) 的操作变得更简单。MLIR 全称是 Multi-Level Intermediate Representation,是一个编译器开发框架,它存在的目的是通过定义多种方言来逐级将代码转换为机器码,以降低编译器的开发成本。在 MLIR 之前,一个广为人熟知的 IR 是 LLVM IR,一个语言的编译器作者可以通过将自己的语言编译为 LLVM IR 来接入 LLVM 的工具链,使得编译器作者不需要关心底层具体硬件的差别,实现了对底层编译工具链的复用:
但 LLVM IR 层级过低,难以进行特定于语言本身的优化,从上面的图中也能看出,各个语言为了实现语言本身的优化,都在编译为 LLVM IR 之前加入了自己的 IR。另外 LLVM IR 扩展起来也非常困难,难以适应复杂异构计算的要求,而异构计算在 AI 开发中又非常普遍。MLIR 相比于之前的 IR,更加模块化,仅保留了一个非常小的内核,方便开发者进行扩展。很多编译器将代码编译为 MLIR,而 Mojo 提供了直接访问 MLIR 的能力,这使得 Mojo 能够受益于这些工具。更多关于 MLIR 的内容可以参考这一系列文章:编译器与中间表示: LLVM IR, SPIR-V, 以及 MLIR,这里就不做过多赘述,我们主要关注在 Mojo 中可以如何操作 MLIR。举例而言,如果我们希望实现一个新的 boolean 类型 OurBool,我们可以这样实现:
alias OurTrue: OurBool = __mlir_attr.`true`alias OurFalse: OurBool = __mlir_attr.`false`@register_passable("trivial")struct OurBool:var value: __mlir_type.i1fn __init__() -> Self:return OurFalsefn __init__(value: __mlir_type.i1) -> Self:return Self {value: value}fn __bool__(self) -> Bool:return Bool(self.value)
这里定义了一个类型为 OurBool 的类型,里面有一个直接使用 MLIR 内置类型 i1 的成员 value 。在 Mojo 中,我们可以通过 __mlir_type.typename 的形式来访问 MLIR 类型。接着,我们为这个类型提供了两个构造函数,默认情况下构造为 OurFalse 也可基于传入的参数进行构建。最下面的 __bool__ 也和 Python 的 __bool__ 一样,用于使该类型具有和内置 boolean 类型的性质,此时我们可以这样使用它:
let t: OurBool = OurTrueif t: print("true") #> true
除了使用 MLIR 之外,Mojo 甚至可以允许开发者使用 MLIR 实现逻辑,例如下面的代码中通过应用 MLIR 的 index.casts 操作来实现类型转换,然后再通过 index.cmp 对值进行比较:
# ...struct OurBool:# ...fn __eq__(self, rhs: OurBool) -> Self:let lhsIndex = __mlir_op.`index.casts`[_type : __mlir_type.index](self.value)let rhsIndex = __mlir_op.`index.casts`[_type : __mlir_type.index](rhs.value)return Self(__mlir_op.`index.cmp`[pred : __mlir_attr.`#index`](lhsIndex, rhsIndex))
基于封装好的 __eq__ 方法,我们可以很容易实现 __invert__ 方法:
# ...struct OurBool:# ...fn __invert__(self) -> Self:return OurFalse if self == OurTrue else OurTrue
此时,我们就可以对 OurBool 类型的对象使用 ~ 操作符了:
let f = OurFalseif ~f: print("false") #> false
通过这个简单的例子我们可以看出,在 Mojo 中,开发者可以通过访问 MLIR 来实现和内置类型同等高效的类型。这使得开发者可以在 Mojo 上为新硬件的数据类型封装高效简单的 Mojo 接口而不需要切换语言。虽然大部分开发者并不需要接触 MLIR,但 Mojo 为更深入和更底层的优化提供了充分的可能性。
虽然 Mojo 反复强调它是为 AI 设计的新语言,但以目前 Mojo 的设计方向来看,它的发展前景并不止于 AI。本质上 Mojo 提供了一个能够兼容 Python 生态的高性能语言,且这个语言可以让 Python 开发者几乎无痛地切换过去,那 Python 开发者何乐而不为呢?对于使用 Mojo 的开发者来说,上层业务可以将 Mojo 当 Python 一样使用,享受到简明的语法带来的高开发效率,当出现性能瓶颈的时候,也不用切换语言去进行优化,直接使用 Mojo 重构模块即可。虽然现在还没法在生产环境中验证这个想法,但这个未来听起来确实非常美好。关于 Mojo 和 Python 开发性能的对比,您可浏览 Mojo 发布会上的 Jeremy Howard demo for Mojo launch 视频。
目前 Mojo 还在比较早期的阶段,不仅许多语言特性都还没实现,而且连本地开发的套件都没有提供。不过其发展路线和设计思路都非常务实 ,又有一个足够专业的领导者和公司作为背景支撑,可以说是未来可期,也非常希望这个语言能在其他领域得到更广泛的应用。
标签:
推荐文章
- 人机对话技术升级 之江实验室获2021年度浙江省科技进步二等奖
- 研究人员最新发现 单个细胞可同时处理成百上千个信号
- 陆军第73集团军某旅 创新升级模拟训练器材
- 长期暴露在光照下性能退化 科学家发现钙钛矿太阳能电池最大缺陷
- 宁夏启动双百科技支撑行动 构建高水平产业创新体系
- 陆军炮兵防空兵学院 毕业学员综合战术演习现地备课工作圆满完成
- 国内首颗以茶叶冠名遥感卫星 安溪铁观音一号发射成功
- 区域特色产业转型升级 四川屏山以“3+”模式推进科技创新工作
- 激发创新动能促进产业发展 无锡滨湖走出产业转型“绿色”路
- 绥化全域低风险!黑龙江绥化北林区一地调整为低风险
- 走访抗美援朝纪念馆:长津湖的寒冷,与战斗一样残酷
- 节后第一天北京白天晴或多云利于出行 夜间起秋雨或再上线
- 走近网瘾少年们:他们沉迷网络的病根何在?
- “双减”后首个长假:亲子游、研学游需求集中释放
- 获2021年诺奖的蛋白,结构由中国学者率先解析
- 他从一窍不通的“门外汉”,到重装空投“兵专家”
- 升旗、巡岛、护航标、写日志,他们一生守护一座岛
- 中国故事丨“沉浸式”盘点今年的教育好声音!
- 农业农村部:确保秋粮丰收到手、明年夏季粮油播种
- “双减”出台两个月,组合拳如何直击减负难点?
- 《山海情》里“凌教授”的巨菌草丰收啦
- 且看新疆展新颜
- 天山脚下,触摸丝路发展新脉动
- 160万骑手疑似“被个体户”?平台不能当甩手掌柜
- 网游新政下,未成年人防沉迷的“主战场”在哪?
- “辱华车贴”商家及客服被行拘,处罚要不放过每一环
- 沙害是自然界的恶魔,而他是荒沙碱滩的征服者
- 面对婚姻,“互联网世代”的年轻人在忧虑什么?
- IP类城市缘何吸引力强?玩法创新带动游客年轻化
- 国庆主题花坛持续展摆至重阳节
- 都市小资还是潮流乐享?花草茶市场呈爆发性增长
- 从1.3万元降到700元,起诉书揭秘心脏支架“玄机”
- 北京国庆7天接待游客超861万人次 冬奥线路受青睐
- 陈毅元帅长子忆父亲叮嘱:你们自己学习要好,就可以做很多事儿
- 报告显示:这个国庆假期,粤川浙桂赣旅游热度最高
- 中国科技人才大数据:广东总量第一,“北上”这类人才多
- 嘉陵江出现有记录以来最强秋汛
- 全国模范法官周淑琴:为乡村群众点燃法治明灯
- 线上教学模式被盯上,网络付费刷课形成灰色产业链
- 云南保山:170公里边境线,4000余人日夜值守
- 警方查处故宫周边各类违法人员12人
- 农业农村部:确保秋粮丰收到手、明年夏季粮油播种
- 受南海热带低压影响 海南海口三港预计停运将持续到10日白天
- 多地网友投诉遭遇旅游消费骗局,呼吁有关部门严查乱象
- 神经科学“罗塞塔石碑”来了:迄今为止最完整的大脑细胞图谱
- 汾河新绛段发生决口
- 陕西支援14省份采暖季保供用煤3900万吨
- 这场红色故事“云比拼”,穿越时空为我们指引方向
- 受琼州海峡封航影响 10月7日、8日进出海南岛旅客列车停运
- 辽宁省工信厅发布10月8日电力缺口橙色预警
- 广州10月8日至20日对所有从省外来(返)穗人员实施核酸检测
- 假期怎么过得这么快?国庆5.15亿人次出游,你咋过的?
- 国庆假期全国道路交通总体安全平稳有序
- 哈尔滨市南岗区爱达88小区将调整为低风险地区
- 新疆霍尔果斯市2例无症状感染者新冠病毒均为德尔塔变异株
- 百闻不如一见——北京大学留学生参访新疆
- 看,生机勃勃的中国
- 国庆假期中国预计发送旅客4.03亿人次
- 新疆兵团可克达拉市:195名密接者已全部隔离医学观察
- 山西平遥消防4天29次救援:拖着腿走路也要完成任务
- 国庆假期北京接待游客861.1万人次
- 冷空气自西向东影响中国大部地区 气温将下降4℃至6℃
- 新疆哈密市巴里坤县发生4.3级地震 震源深度9千米
- 国庆假期中国国内旅游出游5.15亿人次
- 公安部交管局:国庆假期日均出动警力18万余人次,5位交警辅警牺牲
- 受南海热带低压影响广东将暂别高温天气
- “数说”杭州无障碍改造:触摸城市“爱的厚度”
- 新疆霍尔果斯无症状感染者新冠病毒属德尔塔变异株 未发现高度同源的基因组序列
- 新疆伊犁州:妥善做好滞留旅客安置返回工作
- 国庆假期广西累计接待游客逾3611万人次 实现旅游消费272.41亿元
- 2021年MAGIC3上海市青少年三对三超级篮球赛落幕
- 新疆兵团第四师可克达拉市1名无症状感染者为餐饮从业人员
- 哥伦比亚遇上广州:洋茶人“云上”喫茶 传播中国茶“味道”
- 厦门同安区四区域调整为低风险 全市无中高风险地区
- 直径2米“面气球”亮相 山西首届“寿阳味道”美食大赛启幕
- 世界第一埋深高速公路隧道大峡谷隧道出口端斜井掘进完成
- 浙南沿海村村发展有妙招 搭乘共富快车打造“海上花园”
- 新疆霍尔果斯两例无症状感染者新冠病毒均属德尔塔变异株
- 南沙港铁路国庆假期不停工 力争今年年底开通
- 添加陌生人为好友 内蒙古两女子被骗126万
- 中国国庆假期出行热:数字改变“关键小事”
- 水能载物亦能“生金” 浙江遂昌山村以水为媒奔共富
- 铁路人国庆雨中巡查排险记:一身雨衣、一把铁锹保安全畅通
- 铁路迎返程高峰 西安局集团公司加开79趟高铁列车
- 受热带低压影响 琼州海峡北岸等待过海车辆排长龙
- 哈尔滨市学校有序恢复线下教学
- 哈尔滨一地风险等级调整为低风险
- 从进“培训班”到看《长津湖》
- 安徽黄山国庆假期迎客12万余人 旅游市场稳步复苏
- 山西解除持续近90小时的暴雨四级应急响应
- 科学拦峰错峰削峰 嘉陵江洪水过境重庆中心城区“有惊无险”
- 粤高速大湾区路段假期车流集中 跨珠江口通道尤甚
- 千年街区“非遗”风催热国庆假期本地游
- “颜值担当”里的中国,映照“万物和谐”新气象
X 关闭
资讯
- 放弃Python拥抱Mojo?鹅厂工程师真实使用感受
- 今后9月,四大生肖子女孝顺,财运大旺,财库大开
- 华映科技(000536):8月9日北向资金减持153.08万股
- 易点天下(301171.SZ)发布半年度业绩,净利润1.19亿元,同比下降32.99%
- 特斯拉在印度租赁办公楼,暗示将进入当地市场
- 裁决之镰怎么减刑(裁决之镰怎么解除)
- 恒大地产所持廊坊发展约7603万股股份被轮候冻结
- 曾黎个人资料简介图片(曾黎个人资料)
- 罗马诺:姆巴佩再次拒绝巴黎续约报价 纳赛尔对球员非常愤怒
- 创新医疗(002173.SZ):3642.09万股限售股份将于8月11日解禁
- 云鲸2023年扫地机新品将于8月发布,官方透露:搭载自集尘功能!
- 韩红评价那英及其作品,短短16个字,却字字珠玑!
X 关闭