爲何Julia這麼快?

我原本說今年不會寫文章了,不過這個編輯器比知乎的好用多了啊!公式也很棒!之後不想再寫知乎了,我來這裏寫量子計算有人看麼?!真香!給大家寫編輯器的程序員加個雞腿好很差!好了回到正題git

這是不少人都會問的一個問題。程序員

我本身一直以爲本身講不清楚。都是codegen,憑啥Julia快,Julia能用LLVM,Python還有別的各類也能用啊?Julia到底有什麼好的?最近由於1.0了因而和一些人討論了下(好比紅紅),而後也Google了一下,這篇文章裏的內容大多整理自這些我聽到和看到的觀點。github

而後我也被糾正了一個觀點:Julia只適合科學計算。聽了這些觀點之後我想Julia若是生態可以作好,在這個階段可以吸引到有技術能力的開發者嘗試或許能夠出現不少不錯的東西。算法

從 Julia 的第一篇論文提及

讓咱們回到Julia的第一篇論文裏去(我只是大概翻譯了部分,感興趣還請去看原文):express

arxiv.org/pdf/1209.51…編程

在文章的開頭部分,能夠看到實際上在一開始 Jeff Bezanson,Stefan Karpinski,Viral B. Shah,Alan Edelman 所嘗試要解決的是通常的兩語言問題,兩語言問題每每表現爲對易用性(Convenience)和性能(performance)的妥協,程序員在須要描述算法的高級(high level)而複雜的邏輯時使用容易使用的動態語言,而在性能敏感的地方每每會使用C,Fortran。這樣的方式對於一些應用頗有效,但也是有缺點的。這在寫一些並行代碼的時候,算法複雜性會變得很大。而編寫向量化的(vectorized)代碼,對於不少問題來講很是的不天然,而且可能會產生本能夠由顯示的for循環所避免的中間變量。數組

因爲須要去考慮兩種語言之間的類型轉換和內存管理,編寫兩種語言的代碼可能會比用任何其中一種語言編寫的代碼都要複雜。而若是處理很差兩層代碼之間的界面,則有可能會大大增長優化的難度。dom

那麼另一種解決方案就是加強咱們已有的動態語言的性能,例如Python,好比像PyPy這樣的工程實際上已經很是成功了[1]。這些已有的工程都是嘗試去優化一個已有的語言,這是很是有用的,由於已有的代碼能夠直接獲益。可是這些方案都沒法真正解決兩語言問題。以解釋器語言爲假設的語言設計決定使得其可以生成高效代碼的能力收到了破壞。正如Henry Baker對Common Lisp的觀察:機器學習

...the polymorphic type complexity of the Common LISP library functions is mostly gratuitous, and both the efficiency of compiled code and the efficiency of the programmer could be increased by rationalizing this complexity. [2][3][4]編程語言

Julia在設計之初就考慮如何讓其利用現代的技術去高效加速動態語言。從結果上來看Julia在提供了像Python,Lisp,Ruby這樣交互式編程和動態性的同時,也有着靜態編譯語言通常地性能。

Julia的性能主要是由這樣三點特性所獲的:

  1. 經過多重派發(multiple dispatch)天然得到地充分的類型信息
  2. 對動態類型激進地(aggressive)代碼特化(code specialization,好比C++的template就是一個例子,注)
  3. 利用LLVM的JIT編譯

實際上到這裏咱們就已經看到Julia的速度不是簡單地靠產生LLVM,而是由語言自己的設計所帶來的

在過去嘗試優化動態語言的動做中,研究人員已經觀察到了實際上程序可能並非程序員們所想象的那麼動態[5]

We found that dynamic features are pervasive throughout the benchmarks and the libraries they include, but that most uses of these features are highly constrained...

從這一點來講,現有的編程語言設計可能並未找到一個良好的平衡點。有不少代碼實際上均可以是靜態類型的,並被更高效地執行。可是因爲語言自己的設計並不能實現這一點。咱們假設如下的 「動態性」 是更加有用的:

  1. 可以在加載和編譯時期運行代碼的能力,這可使得編譯系統和配置文件更容易
  2. 將一個通常的任意類型(Any type)做爲惟一的真正的靜態類型,使得能夠在須要的時候忽略靜態類型
  3. 不要拒絕形式上優美的代碼
  4. 程序行爲僅僅由運行時的類型決定(例如沒有靜態重載)

而Julia拒絕了一些阻礙優化的特性(例如CLOS [6]),而有以下的限制:

  1. 類型自己是不可變的
  2. 一個值的類型在其生存週期內是不可變的
  3. 局部變量的環境不會被具體化(reified)
  4. 程序代碼不可變(注,可是能夠產生新的代碼而後被執行,這大概體如今宏的world裏)
  5. 不是全部的綁定都是可變的(容許定義常數)

這些限制使得編譯器能夠看到全部具體的本地變量,而後僅僅根據局部信息就能夠進行分析。

文章我就不全翻譯了,那麼Stefan在mail list裏大概總結了一下,Julia的性能主要是由如下幾點帶來的:

  1. an expressive parametric type system, allowing optional type annotation
  2. multiple dispatch using type annotations to select implementations
  3. dataflow type inference, allowing most expressions to be concretely typed
  4. careful design of the language and standard library to allow analysis
  5. aggressive code specialization on run-time types
  6. just-in-time compilation (using LLVM).

能夠看到做爲語言自己特性的參數類型系統和多重派發(這甚至直接影響了Julia代碼編寫時的設計)是很是重要的。

同時Stefan也評論:

LLVM is great, but it's not magic. Using it does not automatically make a language implementation fast. What LLVM provides is freedom from doing your own native code generation, as well as a number standard low-level optimizations. You still have to generate good LLVM code. The LLVM virtual machine is a typed register machine with an infinite number of write-once registers – it's easier to work with than actual machine code, but not that far removed (which is the whole point).

實際上咱們能夠看到,說如今codegen已經爛大街的言論是很是淺薄的。而認爲Julia毫無創新只是C++,R,Python的混合的言論也是(沒法描述)的。

總結一下,Julia其實是對本來的一些動態語言作了一些限制的結果,它在嘗試尋找一個更優的平衡點。說它繼承了Python的簡單是錯誤的,說它繼承了R也是錯誤的,Julia也更沒有繼承C++。Julia所想表達的是,咱們也許能夠犧牲一些不那麼重要的動態性,就可以換來很是驚人的速度。

至於這樣的平衡是否就是最優的,那麼就仁者見仁智者見智了吧。

一些嘗試

那麼實際上,有一些嘗試挑戰C/Fortran的嘗試:

純Julia實現一個BLAS:

discourse.julialang.org/t/we-can-wr…

純Julia實現的一個HDF5:

github.com/simonster/J…

純Julia實現的一個JSON(根據紅紅評價,這個他能夠作的更好):

github.com/quinnj/JSON…

從這一點看來,個人認識其實以前也是不正確的,除了更加統一的多維數組(這對物理學家很是重要,否則也不會有那麼多人還用Fortran)之外,也許咱們還能夠有更加普遍的應用,這不只僅限於科學計算,機器學習,而是更多的過去須要兩語言問題來解決的地方。

可是相對的,過去用一種語言就能夠解決的問題,或許這樣一個大殺器也用起來並不順手。我想這樣大概足夠客觀地描述Julia了,你們也能夠從中去體會到它適合什麼場景不適合什麼場景。

最後,我我的以爲,目前Julia不適合小白。也不適合想要找工做的人。可是它更適合那些過去被兩語言問題所折磨的人。


[1]: C. F. Bolz, A. Cuni, M. Fijalkowski, and A. Rigo. Tracing the meta-level: Pypy’s tracing jit compiler. In Proceedings of the 4th workshop on the Implementation, Compilation, Optimization of Object-Oriented Languages and Programming Systems, ICOOOLPS ’09, pages 18–25, New York, NY, USA, 2009. ACM.

[2]: H. G. Baker. The nimble type inferencer for common lisp-84. Technical report, Tech. Rept., Nimble Comp, 1990.

[3]: R. A. Brooks and R. P. Gabriel. A critique of common lisp. In Proceedings of the 1984 ACM Symposium on LISP and functional programming, LFP ’84, pages 1–8, New York, NY, USA, 1984. ACM.

[4]: F. Morandat, B. Hill, L. Osvald, and J. Vitek. Evaluating the design of the R language. In J. Noble, editor, ECOOP 2012 Object-Oriented Programming, volume 7313 of Lecture Notes in Computer Science, pages 104–131. Springer Berlin / Heidelberg, 2012.

[5]: M. Furr, J.-h. D. An, and J. S. Foster. Profile-guided static typing for dynamic scripting languages. SIGPLAN Not., 44:283–300, Oct. 2009.

[6]: H. G. Baker. Clostrophobia: its etiology and treatment. SIGPLAN OOPS Mess., 2(4): 4–15, Oct. 1991.

相關文章
相關標籤/搜索