農林漁牧網

您現在的位置是:首頁 > 畜牧業

老碼農告訴你什麼是Dart

2022-02-05由 程式碼以外 發表于 畜牧業

rpr是什麼意思

開發者體驗和執行時效率

在沒有太多接觸 dart 的時候,我想當然以「既生瑜何生亮」為由不喜歡這門語言。因為嘗試 flutter 而「不得不」使用 dart 後,我開始慢慢欣賞這門語言。dart 和我之前使用過的很多語言都不太一樣:有些語言顧及到開發時效率,如 python/javascript/elixir,卻付出了執行時效率作為代價;有些語言顧及到了執行時效率,卻讓開發效率受到損傷,如 c/golang/rust。在我們平時的觀念裡,開發效率和執行時效率,就像魚和熊掌,二者不可兼得。而 dart 是少見地想把兩者都佔全了。

當我們講一門語言的效能時,我們往往談及的是:

1。更小的程式碼體積

2。更快的啟動時間

3。更高的吞吐量

4。更低的延遲

這些要素在開發時和執行時的要求是不一樣的,而我們往往只考慮了執行時的需求而忽視了開發時的需求:

更小的程式碼體積:對於開發時而言,程式碼體積並不重要。

更快的啟動時間:對於開發時而言,啟動時間很重要,尤其是重新載入所花費的啟動時間,以及恢復到上一次執行狀態的時間。

更高的吞吐量:對於開發時而言,吞吐量也不重要。

更低的延遲:對於開發時而言,程式在修改之後,到改動得以體現之間的延時更為重要。

dart 為兩種截然不同的需求提供了截然不同的解決方案:

開發時:JIT 編譯器,如 dart VM,dartdevc。

執行時:AOT 編譯器,如 dart2native,dart2js。

JIT 編譯器的目的很單純,把你剛剛撰寫的程式碼儘快編譯成目標平臺的程式碼。因為要快,所以它會犧牲很多解析,分析和最佳化的步驟,對於開發者來說,JIT 可以帶來更低的開發延遲,而對於使用者來說,JIT 沒有太多好處,效率不高,冷啟動速度還慢,對使用者不太友好。而 AOT 編譯器則要把編譯原理課程裡的所有步驟都走一遍,甚至有些步驟要來回走很多遍(比如 rust)。這對使用者非常友好,大大提升了冷啟動的速度,執行效率更高,然而 AOT 對開發者很不友好 —— 看看下圖額外的啟動時間和執行時間:

老碼農告訴你什麼是Dart

如此一來,從產品的角度來看,開發者和開發者的使用者的利益都兼顧到了,使用者體驗非常美妙,可是 dart 團隊需要做的工作就多了很多。一個新的語言特性需要被新增到不同的編譯器之中,需要考慮不同的場景下的最佳化方法。隨著 dart 對原生平臺的支援力度越來越大,支援的平臺越來越多,這樣的工作會越來越繁瑣。我想,這也是大部分語言只照顧一頭的原因。

語言特性

大部分時候,flutter 中使用到的 dart 都是在畫 UI,而這部分的語法,有程式設計基礎的人看著例子十分鐘內都能上手。但既然因為嘗試 flutter 而使用 dart,那麼 dart 的語言特性還是需要大致瞭解一下的。

dart 面向物件的特性沒有太多可說的,如果你有 java/C# 背景,裡面的介面,泛型和型別系統都不難理解,大家基本大同小異。如果你來自前端世界,有 kotlin / swift 背景,或者出道於後端,是 rust / scala / haskell 的擁躉,那你大機率會對 dart 的型別系統有些失望,因為 dart 在語言層面沒有完整支援 ADT(algebra data types),只有 product type(class),卻沒有 sum type(tagged union),使得你不太容易優雅地表述複雜的,帶有「或」關係的資料結構。而模式匹配,因為它往往和 sum type 是孿生兄弟,在 dart 裡也沒有支援。

dart 的併發模型非常討喜,至少,對我的胃口。它受 erlang 的影響不小,提供了類似於 erlang process 的 isolate。在 dart 裡,每個 isolate 都有自己的棧和堆,isolate 之間 “share nothing”,只能透過傳送和接收訊息來傳遞資料。每個 isolate 自己單獨做 GC,這和 erlang 的 GC 也非常類似,因而記憶體的分配和回收無需加鎖,很大程度上避免了 Java 的 STW 問題。dart 裡 isolate 之間的通訊見下圖,熟悉 erlang VM 的小夥伴估計都會會心一笑:

老碼農告訴你什麼是Dart

dart 每個 isolate 內部,執行一個 event loop,處理這個 isolate 上的事件。和 javascript 一樣,dart 裡的每個非同步事件都是一個 future 物件,語言本身提供 async/await 作為語法糖。在 web 環境下,isolate 會被 web worker 執行;而在原生環境下,isolate 可能會被某個執行緒呼叫,但要注意的是:同一個執行緒不能在同一時間處理兩個不同的 isolate。

dart 還有一個有意思的特性是 snapshot。顧名思義,snapshot 允許 dart 儲存並序列化當前 VM 的上下文,下次可以從 snapshot 中恢復執行。snapshot 目前主要用作加快 dart 應用的啟動,但也許未來可以用於很多有意思的場合:比如 bug 的復現 —— 複雜的 bug 可以透過儲存 snapshot 給程式設計師輕鬆復現。

老碼農告訴你什麼是Dart

執行時

在開發環境下,dart 執行時包括公共前端(Common Front-End,CFE)和 VM。dart CFE 提供了程式碼編譯服務(compiler)以及程式碼分析服務(analyzer),其中,程式碼分析服務是提供給 vscode / android studio 這樣的程式碼編輯器的。dart 的程式碼分析服務做得相當出色,無論是型別推導,還是自動補全,還是程式碼跳轉,相對於我比較常用的語言 elixir 和 rust 來說,反應速度都是一流,從不卡頓。這使得我在 vscode 裡撰寫 dart 程式碼的體驗非常舒服。尤其是 dart 2。5 以後,其 CFE 的程式碼分析服務還內建了 tensorflow lite,用於基於機器學習的程式碼自動補全。這是一個從使用者需求考慮產品的極致的殺手級功能 —— 我想不出還有什麼程式語言的前端會如此照顧使用者體驗 —— 可以肯定的是未來會有更多的程式語言在這一塊上迅速跟進。我相信,隨著大家在各種語言的 CFE 上的機器學習能力的投入,以後我們寫程式碼會越來越輕鬆。

老碼農告訴你什麼是Dart

在執行 dart 程式碼時,dart 原始碼經過 CFE 被翻譯成 kernel binary,交給 VM 執行:

老碼農告訴你什麼是Dart

這個過程幾乎所有的 JIT 語言都有類似的處理方式。

然而 ——

dart 還有另一種玩法,就是我們執行 flutter 在裝置模擬器上執行程式碼的方式:

老碼農告訴你什麼是Dart

乍一看,android / ios 不也是類似的方式和模擬器互動麼:在使用者的作業系統上交叉編譯出目標系統上的程式碼,將其同步到目標系統上執行。但 dart JIT 的方便之處在此顯現:第一次完整編譯之後,程式碼的改動都只需要編譯和傳送修改的部分,VM 負責在目標系統上更新修改的程式碼。於是,程式可以在維持已有的狀態的情況下,得到更新 —— 這就是 flutter 引以為傲的殺手鐧:hot reload。看得出,dart 深度借鑑了 erlang 的 code server 的 hot reload 特性,並且將 hot reload 的能力在客戶端開發上淋漓盡致地表現出來。

結論

這一週在 flutter 上的實驗,讓我對 dart 的好感度提升了很多。儘管 dart 有這樣那樣的不足,但不得不說,它具有獨特的魅力。有時候我們真的要承認每個人都有「未知的無知」—— you don‘t know what you don’t know。程式語言和人很類似,在沒有把它放在合適的位置之前,就像莊子說的「不龜手之藥」一樣,看上去是那麼的無用。所以我們常常感慨:

千里馬常有,而伯樂不常有

。感謝 flutter,讓我有機會粗淺地研究 dart,從而彌補了一些我認知上的盲區

參考文獻

Dart: A Language For Structured Web Programming: https://www。youtube。com/watch?v=UZOIAz-eR34

Google will note integrate its dart programming language into chrome: https://techcrunch。com/2015/03/25/google-will-not-integrate-its-dart-programming-language-into-chrome/

Dart: Productive, Fast, Multi-Platform - Pick 3: https://www。youtube。com/watch?v=J5DQRPRBiFI

Introduction to Dart VM: https://mrale。ph/dartvm/

Announcing Dart 2。5: Super-charged development: https://medium。com/dartlang/announcing-dart-2-5-super-charged-development-328822024970

Aqueduct framework: https://aqueduct。io/

Angel framework: https://angel-dart。dev/

How to call a rust function from dart using ffi: https://itnext。io/how-to-call-a-rust-function-from-dart-using-ffi-f48f3ea3af2c