伊人久久夜,精品一区二区三区视频,鲁一鲁中文字幕久久,国产第一页精品,aiai永久免费网站在线观看,a毛片在线还看免费网站,a级免费视频

放棄Python擁抱Mojo?鵝廠工程師真實(shí)使用感受

# 關(guān)注并星標(biāo)騰訊云開(kāi)發(fā)者


(資料圖)

# 每周1 | 鵝廠工程師帶你審判技術(shù)

#第1期|李志瑞:AI 屆新語(yǔ)言 Mojo 要火??

前段時(shí)間 Modular 發(fā)布了一個(gè)新語(yǔ)言 Mojo,這語(yǔ)言不止官網(wǎng)放了巨大的 emoji ?,而且它的標(biāo)準(zhǔn)文件后綴一個(gè)是「.mojo」另一個(gè)是「.?」,一副立馬要火的樣子呢。

說(shuō)實(shí)話,這個(gè)用 emoji 做后綴名的操作其實(shí)挺無(wú)聊,也有點(diǎn)敗好感,但如果說(shuō)這個(gè)語(yǔ)言能在完全兼容 Python 的基礎(chǔ)上大幅提高執(zhí)行效率,并且作者是 LLVM 發(fā)起人 Chris Lattner,是不是突然又有興趣繼續(xù)了解它了呢?

Mojo 被設(shè)計(jì)為 Python 語(yǔ)言的超集,并增加了許多特性,包括:

?? Progressive types:能利用類型信息獲得更好性能和靜態(tài)檢查,但又不強(qiáng)制要求寫類型。

?? Zero cost abstractions:C++ 的核心設(shè)計(jì)準(zhǔn)則,能夠避免用戶為了性能放棄合理設(shè)計(jì)的代碼。

?? Ownership + borrow checker:Rust 語(yǔ)言的安全性來(lái)源,在編譯期避免許多錯(cuò)誤的發(fā)生。

?? The full power of MLIR:原生支持對(duì) MLIR 的直接訪問(wèn),能夠從底層擴(kuò)展系統(tǒng)。

在 Mojo 這個(gè)語(yǔ)言的介紹中反復(fù)提到 AI,官網(wǎng)也說(shuō)它是「a new programming language for all AI developers」。那么為什么 AI 開(kāi)發(fā)需要一個(gè)新語(yǔ)言呢?首先,我們知道在 AI 屆具有統(tǒng)治地位的語(yǔ)言就是 Python,Python 是一個(gè)語(yǔ)法簡(jiǎn)單清晰,容易上手,且靈活度很高的語(yǔ)言,深受廣大程序員喜愛(ài),XKCD 上有就這么一幅漫畫:

當(dāng)然,受人喜愛(ài)的語(yǔ)言有很多,Python 成為 AI 屆的統(tǒng)治語(yǔ)言除了本身易用之外,也有慣性的因素。由于 Python 上機(jī)器學(xué)習(xí)相關(guān)的庫(kù)多,因此機(jī)器學(xué)習(xí)從業(yè)者用的就多,這又反過(guò)來(lái)令新的機(jī)器學(xué)習(xí)相關(guān)庫(kù)優(yōu)先為 Python 提供接口,進(jìn)一步加強(qiáng)了其統(tǒng)治地位。因此,為了逐步滲透這個(gè)用戶群,Mojo 兼容 Python 是很正確的一個(gè)選擇。Mojo 不僅承諾語(yǔ)法是 Python 的超集,并且它還能直接調(diào)用 Python 的庫(kù),這意味著 Mojo 不需要從零開(kāi)始構(gòu)建自己的生態(tài),本身就可以用上繁榮的 Python 生態(tài)了。

雖然 Python 很好,但它有一個(gè)眾所周知的問(wèn)題,那就是太慢了。而機(jī)器學(xué)習(xí)本身又需要繁重的計(jì)算,因此 Python 生態(tài)中大量庫(kù)的底層其實(shí)都是用高性能的語(yǔ)言(如 C/C++)進(jìn)行實(shí)現(xiàn),然后再提供一個(gè) Python 接口供用戶調(diào)用,典型的如 numpy 這種數(shù)學(xué)庫(kù)。在這種情況下,Python 事實(shí)上是被作為一個(gè)膠水語(yǔ)言來(lái)使用,這造成了開(kāi)發(fā)的碎片化,如果一個(gè)用戶只是簡(jiǎn)單調(diào)一下庫(kù)那還好說(shuō),但一旦到了工業(yè)界,開(kāi)發(fā)過(guò)程中不可避免地就要涉及一些底層庫(kù)的修改,甚至直接換語(yǔ)言來(lái)實(shí)現(xiàn)同樣的功能以提高性能,這種割裂不止增加了開(kāi)發(fā)成本和精神負(fù)擔(dān),而且考慮到眾多擅長(zhǎng) C/C++ 語(yǔ)言的開(kāi)發(fā)者也并不是 AI 領(lǐng)域?qū)<?,這種開(kāi)發(fā)人員能力的不適配也對(duì)整個(gè) AI 生態(tài)的發(fā)展形成了一定阻礙。

因此,Mojo 的目的就是要在 Python 生態(tài)的基礎(chǔ)上,讓用戶能用一個(gè)語(yǔ)言,從使用易用的接口,到開(kāi)發(fā)復(fù)雜的庫(kù),再到實(shí)現(xiàn)底層黑科技,統(tǒng)一實(shí)驗(yàn)和生產(chǎn)環(huán)境所用的語(yǔ)言。為了實(shí)現(xiàn)這個(gè)目的,Mojo 擴(kuò)展了 Python 語(yǔ)法,支持了緊湊的內(nèi)存布局,并引入了一些現(xiàn)代的語(yǔ)言特性(例如 Rust 的安全性檢查),使得這個(gè)語(yǔ)言能夠漸進(jìn)式地在 AI 界立足。說(shuō)起來(lái) Chris Lattner 在這方面可以算是經(jīng)驗(yàn)豐富了,不管是在 gcc/msvc 的統(tǒng)治下實(shí)現(xiàn) clang,還是在 objective-c 的統(tǒng)治下為蘋果實(shí)現(xiàn) swift,都是一個(gè)逐步蠶食對(duì)手市場(chǎng)的過(guò)程。

說(shuō)了這么多,該來(lái)看看 Mojo 長(zhǎng)什么樣了?,F(xiàn)在 Mojo 還不能直接下載使用,如果想要嘗鮮,需要在官網(wǎng)申請(qǐng),然后在 playground 頁(yè)面中試用,這是一個(gè)基于 Jupyter 的頁(yè)面,可以混合筆記和可執(zhí)行的 Mojo 代碼。

前面提到,Mojo 的語(yǔ)法是 Python 的超集,因此 Mojo 的 Hello World 也跟 Python 一樣簡(jiǎn)單:

print("Hello World") #> Hello World

與 Python 一樣,Mojo 也使用換行符和縮進(jìn)來(lái)定義代碼塊:

fn foo():var x: Int = 1x += 1let y: Int = 1print(x, y)  #> 2 1foo()

上面的代碼中使用 var 來(lái)聲明變量 x,使用 let 來(lái)聲明了不可變量 y。Mojo 像很多較新近的語(yǔ)言一樣,讓不可變量的聲明變得簡(jiǎn)單,以鼓勵(lì)開(kāi)發(fā)者使用不可變的量。另外注意到這里定義函數(shù)使用了 fn 而非 Python 的 def,這是因?yàn)?Mojo 希望在兼容 Python 的基礎(chǔ)上加入編譯期的檢查和優(yōu)化,而 Python 過(guò)于動(dòng)態(tài)的語(yǔ)法很難支持這一目標(biāo),因此,Mojo 同時(shí)支持使用 fn 和 def 兩個(gè)關(guān)鍵字來(lái)聲明函數(shù),對(duì)于調(diào)用者來(lái)說(shuō),這兩種方法聲明出來(lái)的函數(shù)沒(méi)有什么區(qū)別,但對(duì)于實(shí)現(xiàn)者來(lái)說(shuō),可以將 fn 看作「嚴(yán)格模式」下的 def,例如下面的代碼會(huì)編譯錯(cuò)誤(如果改成用 def 則不會(huì)出錯(cuò)):

fn foo(): x = 1 print(x) #error:Expression[12]:6:5:useofunknowndeclaration"x","fn"declarationsrequireexplicitvariabledeclarations#x=1#^

雖然官方承諾 Mojo 的語(yǔ)法是 Python 的超集,但目前 Mojo 還在開(kāi)發(fā)中,很多 Python 語(yǔ)法都還不支持,例如目前連 Python 的 class 都無(wú)法被編譯通過(guò):

class MyClass:def foo():pass# error: Expression [15]:17:5: classes are not supported yet#     class MyClass:#     ^

不過(guò),Mojo 現(xiàn)在先提供了另一個(gè)用來(lái)組織數(shù)據(jù)的關(guān)鍵字 struct,相比于 class,struct 更加靜態(tài)可控,便于優(yōu)化。一方面,struct 支持類似 Python class 風(fēng)格的函數(shù)聲明和運(yùn)算符重載。而另一方面,struct 又類似于 C++ 的 struct 和 class,內(nèi)部的成員在內(nèi)存中緊湊排布,而且不支持在運(yùn)行時(shí)動(dòng)態(tài)添加成員和方法,便于編譯期進(jìn)行優(yōu)化,例如:

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

雖然有點(diǎn)不同,但整體上看起來(lái)還是非常熟悉的對(duì)吧。說(shuō)到這里,有一點(diǎn)需要提醒各位注意,盡管 Mojo 之后會(huì)令語(yǔ)法成為 Python 語(yǔ)法的超集,但其語(yǔ)義則有時(shí)會(huì)和 Python 不同,這意味著 Python 的代碼直接拷到 Mojo 里可能會(huì)出現(xiàn)編譯通過(guò)但執(zhí)行結(jié)果不同的情況,這里簡(jiǎn)單提一個(gè)比較常見(jiàn)的例子:函數(shù)傳參。在 Python 中,函數(shù)傳參的語(yǔ)義類似于 C++ 的傳指針,在函數(shù)內(nèi)部雖然不能更改調(diào)用者指向的對(duì)象,但可以改變?cè)搶?duì)象內(nèi)部的狀態(tài),例如下面的代碼:

def foo(lst):lst[0] = 5print(lst)x = [1, 2, 3]foo(x)print(x)

在 Python 中,這段代碼打印出來(lái)的結(jié)果是兩次 [5, 2, 3]。但在 Mojo 中,使用 def 定義的函數(shù)默認(rèn)的傳遞邏輯是復(fù)制值,也就是說(shuō),盡管在函數(shù)中能夠修改參數(shù)內(nèi)部的狀態(tài),但修改對(duì)于調(diào)用方來(lái)說(shuō)是不可見(jiàn)的,因此上面這段代碼在 Mojo 中打印的結(jié)果是 [5, 2, 3](foo 內(nèi)部)和 [1, 2, 3](foo 外部)。

除了語(yǔ)法像 Python,Mojo 非常務(wù)實(shí)的一點(diǎn)在于它構(gòu)建于 Python 的生態(tài)之上。因此即便 Mojo 還沒(méi)能完整支持 Python 的語(yǔ)法,它還是優(yōu)先支持了對(duì) Python 庫(kù)的調(diào)用,以便讓開(kāi)發(fā)者能受益于龐大完善的 Python 的生態(tài)。例如下面的代碼就使用了 Python 的 numpy 庫(kù):

fromPythonInterfaceimportPythonlet np = Python.import_module("numpy")ar = np.arange(15).reshape(3, 5)print(ar.shape)   #> (3, 5)

Mojo 作為一個(gè)新語(yǔ)言,廣泛吸收許多現(xiàn)代的程序語(yǔ)言設(shè)計(jì)思想,例如 Rust 的所有權(quán)和借用檢查,以此提升代碼的安全性。在 Mojo 中,使用 fn 定義的函數(shù)的參數(shù)默認(rèn)傳的是不可變的引用,即「借用」,調(diào)用方仍然擁有其所有權(quán),因此在函數(shù)內(nèi)部不可以對(duì)參數(shù)進(jìn)行修改。Mojo 提供了一個(gè) borrow 關(guān)鍵字來(lái)標(biāo)注這樣的參數(shù)傳遞情況,對(duì)于 fn 來(lái)說(shuō)是可以省略的,也就是說(shuō)下面 foo 函數(shù)中兩個(gè)參數(shù)的傳遞方式相同:

fn foo(borrowed a: SomethingBig, b: SomethingBig):a.use()b.use()

在 Rust 中,傳參的默認(rèn)行為是移動(dòng),如果需要借用則需要在傳入時(shí)加上 &,這兩種方式倒是沒(méi)有太大的優(yōu)劣之分,Mojo 的行為可能更接近于 Python 這類高級(jí)語(yǔ)言的習(xí)慣。如果想要修改傳入的參數(shù),則需要手動(dòng)注明 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()

按道理說(shuō),Mojo 應(yīng)該像 Rust 一樣規(guī)避一個(gè)變量同時(shí)被可變和不可變借用,也應(yīng)該規(guī)避同時(shí)被可變借用,但目前 Mojo 編譯器似乎還沒(méi)實(shí)現(xiàn)這一特性,例如下面的代碼還是能編譯通過(guò)的:

var x = 42swap(x,x)

從這也可以看出 Mojo 確實(shí)還處在比較早期的發(fā)展階段。

另一個(gè)重要的內(nèi)存安全概念是對(duì)象的所有權(quán),當(dāng)一個(gè)函數(shù)獲取了對(duì)象的所有權(quán)后,調(diào)用方就不應(yīng)該再去使用這個(gè)對(duì)象了,例如我們實(shí)現(xiàn)了一個(gè)只支持移動(dòng)的類型 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

同時(shí),我們有兩個(gè)函數(shù),其中,use_ptr 使用了前面提到的 borrow 關(guān)鍵字,借用了 UniquePtr 對(duì)象,而 take_ptr 則使用 owned 關(guān)鍵字,指明它需要獲取傳入對(duì)象的所有權(quán)。那么,在調(diào)用 take_ptr 的時(shí)候,我們就需要在參數(shù)后面加上 ^ 后綴,用來(lái)表明我們將所有權(quán)轉(zhuǎn)移給 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 的調(diào)用順序調(diào)換一下,就會(huì)出現(xiàn)編譯錯(cuò)誤:

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 的另一個(gè)強(qiáng)大之處在于它讓對(duì) MLIR>) 的操作變得更簡(jiǎn)單。MLIR 全稱是 Multi-Level Intermediate Representation,是一個(gè)編譯器開(kāi)發(fā)框架,它存在的目的是通過(guò)定義多種方言來(lái)逐級(jí)將代碼轉(zhuǎn)換為機(jī)器碼,以降低編譯器的開(kāi)發(fā)成本。在 MLIR 之前,一個(gè)廣為人熟知的 IR 是 LLVM IR,一個(gè)語(yǔ)言的編譯器作者可以通過(guò)將自己的語(yǔ)言編譯為 LLVM IR 來(lái)接入 LLVM 的工具鏈,使得編譯器作者不需要關(guān)心底層具體硬件的差別,實(shí)現(xiàn)了對(duì)底層編譯工具鏈的復(fù)用:

但 LLVM IR 層級(jí)過(guò)低,難以進(jìn)行特定于語(yǔ)言本身的優(yōu)化,從上面的圖中也能看出,各個(gè)語(yǔ)言為了實(shí)現(xiàn)語(yǔ)言本身的優(yōu)化,都在編譯為 LLVM IR 之前加入了自己的 IR。另外 LLVM IR 擴(kuò)展起來(lái)也非常困難,難以適應(yīng)復(fù)雜異構(gòu)計(jì)算的要求,而異構(gòu)計(jì)算在 AI 開(kāi)發(fā)中又非常普遍。MLIR 相比于之前的 IR,更加模塊化,僅保留了一個(gè)非常小的內(nèi)核,方便開(kāi)發(fā)者進(jìn)行擴(kuò)展。很多編譯器將代碼編譯為 MLIR,而 Mojo 提供了直接訪問(wèn) MLIR 的能力,這使得 Mojo 能夠受益于這些工具。更多關(guān)于 MLIR 的內(nèi)容可以參考這一系列文章:編譯器與中間表示: LLVM IR, SPIR-V, 以及 MLIR,這里就不做過(guò)多贅述,我們主要關(guān)注在 Mojo 中可以如何操作 MLIR。舉例而言,如果我們希望實(shí)現(xiàn)一個(gè)新的 boolean 類型 OurBool,我們可以這樣實(shí)現(xiàn):

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)

這里定義了一個(gè)類型為 OurBool 的類型,里面有一個(gè)直接使用 MLIR 內(nèi)置類型 i1 的成員 value 。在 Mojo 中,我們可以通過(guò) __mlir_type.typename 的形式來(lái)訪問(wèn) MLIR 類型。接著,我們?yōu)檫@個(gè)類型提供了兩個(gè)構(gòu)造函數(shù),默認(rèn)情況下構(gòu)造為 OurFalse 也可基于傳入的參數(shù)進(jìn)行構(gòu)建。最下面的 __bool__ 也和 Python 的 __bool__ 一樣,用于使該類型具有和內(nèi)置 boolean 類型的性質(zhì),此時(shí)我們可以這樣使用它:

let t: OurBool = OurTrueif t: print("true")  #> true

除了使用 MLIR 之外,Mojo 甚至可以允許開(kāi)發(fā)者使用 MLIR 實(shí)現(xiàn)邏輯,例如下面的代碼中通過(guò)應(yīng)用 MLIR 的 index.casts 操作來(lái)實(shí)現(xiàn)類型轉(zhuǎn)換,然后再通過(guò) index.cmp 對(duì)值進(jìn)行比較:

# ...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__ 方法,我們可以很容易實(shí)現(xiàn) __invert__ 方法:

# ...struct OurBool:# ...fn __invert__(self) -> Self:return OurFalse if self == OurTrue else OurTrue

此時(shí),我們就可以對(duì) OurBool 類型的對(duì)象使用 ~ 操作符了:

let f = OurFalseif ~f: print("false")  #> false

通過(guò)這個(gè)簡(jiǎn)單的例子我們可以看出,在 Mojo 中,開(kāi)發(fā)者可以通過(guò)訪問(wèn) MLIR 來(lái)實(shí)現(xiàn)和內(nèi)置類型同等高效的類型。這使得開(kāi)發(fā)者可以在 Mojo 上為新硬件的數(shù)據(jù)類型封裝高效簡(jiǎn)單的 Mojo 接口而不需要切換語(yǔ)言。雖然大部分開(kāi)發(fā)者并不需要接觸 MLIR,但 Mojo 為更深入和更底層的優(yōu)化提供了充分的可能性。

雖然 Mojo 反復(fù)強(qiáng)調(diào)它是為 AI 設(shè)計(jì)的新語(yǔ)言,但以目前 Mojo 的設(shè)計(jì)方向來(lái)看,它的發(fā)展前景并不止于 AI。本質(zhì)上 Mojo 提供了一個(gè)能夠兼容 Python 生態(tài)的高性能語(yǔ)言,且這個(gè)語(yǔ)言可以讓 Python 開(kāi)發(fā)者幾乎無(wú)痛地切換過(guò)去,那 Python 開(kāi)發(fā)者何樂(lè)而不為呢?對(duì)于使用 Mojo 的開(kāi)發(fā)者來(lái)說(shuō),上層業(yè)務(wù)可以將 Mojo 當(dāng) Python 一樣使用,享受到簡(jiǎn)明的語(yǔ)法帶來(lái)的高開(kāi)發(fā)效率,當(dāng)出現(xiàn)性能瓶頸的時(shí)候,也不用切換語(yǔ)言去進(jìn)行優(yōu)化,直接使用 Mojo 重構(gòu)模塊即可。雖然現(xiàn)在還沒(méi)法在生產(chǎn)環(huán)境中驗(yàn)證這個(gè)想法,但這個(gè)未來(lái)聽(tīng)起來(lái)確實(shí)非常美好。關(guān)于 Mojo 和 Python 開(kāi)發(fā)性能的對(duì)比,您可瀏覽 Mojo 發(fā)布會(huì)上的 Jeremy Howard demo for Mojo launch 視頻。

目前 Mojo 還在比較早期的階段,不僅許多語(yǔ)言特性都還沒(méi)實(shí)現(xiàn),而且連本地開(kāi)發(fā)的套件都沒(méi)有提供。不過(guò)其發(fā)展路線和設(shè)計(jì)思路都非常務(wù)實(shí) ,又有一個(gè)足夠?qū)I(yè)的領(lǐng)導(dǎo)者和公司作為背景支撐,可以說(shuō)是未來(lái)可期,也非常希望這個(gè)語(yǔ)言能在其他領(lǐng)域得到更廣泛的應(yīng)用。

關(guān)鍵詞:

來(lái)源:程序員客棧
編輯:GY653

免責(zé)聲明:本網(wǎng)站內(nèi)容主要來(lái)自原創(chuàng)、合作媒體供稿和第三方自媒體作者投稿,凡在本網(wǎng)站出現(xiàn)的信息,均僅供參考。本網(wǎng)站將盡力確保所提供信息的準(zhǔn)確性及可靠性,但不保證有關(guān)資料的準(zhǔn)確性及可靠性,讀者在使用前請(qǐng)進(jìn)一步核實(shí),并對(duì)任何自主決定的行為負(fù)責(zé)。本網(wǎng)站對(duì)有關(guān)資料所引致的錯(cuò)誤、不確或遺漏,概不負(fù)任何法律責(zé)任。任何單位或個(gè)人認(rèn)為本網(wǎng)站中的網(wǎng)頁(yè)或鏈接內(nèi)容可能涉嫌侵犯其知識(shí)產(chǎn)權(quán)或存在不實(shí)內(nèi)容時(shí),應(yīng)及時(shí)向本網(wǎng)站提出書面權(quán)利通知或不實(shí)情況說(shuō)明,并提供身份證明、權(quán)屬證明及詳細(xì)侵權(quán)或不實(shí)情況證明。本網(wǎng)站在收到上述法律文件后,將會(huì)依法盡快聯(lián)系相關(guān)文章源頭核實(shí),溝通刪除相關(guān)內(nèi)容或斷開(kāi)相關(guān)鏈接。

  • 相關(guān)推薦

相關(guān)詞