Python狀況:為什麼PyPy是Python的未來?

pythontab發表於2013-07-11

Python 現在已經不僅僅是膠水指令碼語言了. 不信?看看下面使用Python的成功案例:

YouTube - 主要由 Python編寫

NASA

Industrial Light & Magic Runs - 電影公司

OpenStack

Sage - 科學軟體及其他 (SciPy, PythonXY)

WEB框架 Django, Pyramid, bottle...

修訂控制系統

其他不錯的軟體

  要是你正在尋找快速介紹美麗的Python語言,我推薦My-Favorite-Python-Things

 高階語言是主流

  目前高階語言可以寫出簡單具有靈活性的程式碼. 所以在快速建立應用程式的時候是一個不錯的選擇, 你不需要花時間來處理各種資料型別 (所有介面示例程式碼都是為了滿足編譯處理). 可能有些人就會爭論了,這個特性會產生有bug的程式碼. 但是 Guido van Rossum說:“生產的程式碼誰會不經過測試呢”? 靜態語言在編譯時期就能處理一些錯誤. 但這並不能檢測出所有的錯誤. 最後你還是得編寫測試程式碼。 而有這個時間完全可以為動態語言寫出測試程式碼. 此外人們還不能設計一個堪稱完美的型別系統. 對此Jim Treavor寫了一些總結。

  新技術允許我們為動態語言設計一個高效的執行環境 (JavaScript V8, LuaJIT, Racket, Common Lisp...), 這也可以和大型的框架競爭 (JVM, .NET, ...)

  所有這一切都使得高階語言越來越流行得在大型企業和日常生活中使用。

  Python能延續傳奇嗎?

  現在Python非常流行,同時它的地位也受到競爭者的挑戰。Python有良好的生態系統,也有大型軟體和社群支援,但它缺乏其競爭者的高效和先進的執行環境。

  Python 作為膠水語言.

  正如我在開頭說的一個特點,Python很容易連線各種編譯庫,這是它作為膠水語言在20年前流行的重要原因。但是目前依然活躍的工具已經很老舊了,你必須花大量精力才能使用它們。

ctypes

c 擴充套件 是邪惡的. 它們繫結到Python的特定版本還不能被重複使用. 更糟糕的是, CPython2和CPython3的c 擴充套件 API不一樣. 想想將庫移植到Python3會是什麼情況吧!

Cython - 這是被設計用來編寫C擴充套件的. 但是我敢確定,使用C擴充套件是你最後想做的事. Cython 是一個需要編譯的外部工具. 它最終的程式碼並沒有動態行為,但是它的語法還需要學習. Cython不支援型別推斷. 使用Cpython你不得不去編譯. Cython也不是一個標準. 它不能作為解釋程式碼來執行. _nuitka_的作者 Kay Hayen在Static Compilation - That is the point總結的非常好.

swig, boost - 這些是非常容易的, 通常修改下C/C++ 程式碼就可以了, 或者寫一些方案檔案.

  相比之下,有很多新的工具能在相同的效能下(甚至超出),更好的處理這些任務。

cffi - 一個能輕鬆處理你的c庫的包。在接觸硬體或者支援其他軟體時你會經常做這樣的事(像資料庫客戶端、驅動程式)。嘗試下在python裡使用它是多麼簡單吧。你不需要寫任何的封裝,型別化程式碼。而且還有 CPython 和 PyPy 的支援。

bitey

  將Python作為你程式碼的核心 - 膠水語言另一面

  膠水語言也有另一面。我們來想想底層高效能程式設計的過程。可能看起來會是下面幾個過程:

構思

很多複雜的底層程式碼和組織機構程式碼。很可能是一堆晦澀的泛型程式碼(為了重用性)。

編寫膠水語言

編譯

執行

極可能會做很多的除錯,然後回去修改,考慮到有這麼多的底層程式碼。

  感謝Python的簡便性、指令碼語言的本質和大量的工具,將他作為你程式碼的模板和核心。這就意味著你只需要寫最少的底層程式碼,讓Python做剩下的事:生成組織程式碼和你的底層程式碼需要的環境。

  這與以往Lisp的理念一樣,程式碼即資料,程式碼能夠被其他正在執行的程式碼理解(程式碼可以作為資料被處理)。因而機器可以理解執行時正在執行的程式碼,並且去最佳化它,透過通常的方式就能得到全部的資料資訊,而不用像C++那樣使用模板。這是C++和其他流行的程式語言所沒有的。最終我們有相對更底層的抽象級別,而執行時資訊相對更豐富,使得編譯器可以:

為未知的硬體做特化 (編碼時),包括支援的資料型別,以及可用的最佳化方法。

自動調整(tuning) (例如為庫提供的資料,如 ATLAS...)

推送更多的資訊給編譯器,得到更好的推理。

人們不用為資料型別煩惱(執行時環境就已經可以保證快速、正確使用資料型別)

  於是整個流程就好像這樣:

想法

一點Python程式碼(最棒的部分),用來構建整個架構。然後是一些底層的程式碼 ,同樣很棒,因為這些程式碼沒有噁心的模板和上下文程式碼。事實上,底層的程式碼也可以透過Python程式碼生成。

執行

除錯,比起前面的步驟時間更短一些

  就效能而言,這樣的過程相比之前的方法有著更好的前景。

  這些是已經用到這種方式的:PyPy, cffi, PyOpenCL, PyCUDA, numba, theano...

  把Python當做一個高速語言

  有很多方法能用Python寫出高速的程式碼。最流行而且仍在廣泛傳播的方法是,用底層語言來寫應用裡最複雜的部分,然後使用,這對python來說無疑是很不幸的事。

  所有Python裡出色的高效的工具都需要許多複雜的c程式碼,這阻礙了其他的貢獻者進來。現在我們想要寫出高速而且美觀的python程式碼。

  有很多工具可以把python程式碼編譯成機器程式碼,比如:Nuitka, Python2C, Shedskin, pythran。我認為它們都是失敗的,當你使用它們的時候,就需要跟動態行為說再見了。他們只支援一部分的python語言,並且離完全支援還有很大距離。我甚至不認為以後他們能做到。另外他們也沒有用那些使JIT(Just-In-Time 執行時編譯執行)的解決方案變得出色的先進的技術和執行時資訊。

 多核程式設計

  這方面,Armins Rigo的文章寫的很棒,可以參考:Multicore Programming in PyPy and CPython

 直譯器的設計

  為了讓下一步的開發更簡單,實現動態語言的最佳狀態,Python需要一個合適的架構。當前CPython的架構過於簡單,因而限制比較大,很難做到像JIT編譯器那樣的功能。下面是一些在增強CPython直譯器效能上的失敗的努力:

psyco (被PyPy代替)

Unladen swallow

消除GIL的很多失敗的嘗試

還有一些嘗試修復CPython一些缺陷的嘗試: Stackless和HotPy,但是Guido (Python之父,仁慈的獨裁者) 的堅持使得這些專案沒有被合併到Python中。(說明一下,HotPy還不是產品級的東西)。

  CPython最大的問題是他的C API,這部分沒有很好的設計。其他部分的實現多少都受此影響。

  我們能做什麼?

在粘結程式碼中推進新工具的使用 ( cffi, bitey)

在公共庫中停止對CPython的底層屬性(C API,C擴充套件)的依賴。作為替代,採用有如下功能的中間工具:

cffi - 簡化對C庫的應用

cython - 編寫可移植的C擴充套件。我並不推薦它用於通常的程式設計,不過它確實在維護C擴充套件方面更好一些,也更簡單。Cython已經有CPython和PyPy後端。

  為何 PyPy 是趨勢?

  PyPy為最佳化和進一步的語言開發提供了更好的架構。對於大部分Python已有的問題,PyPy已經提供瞭解決方案:

先進的runtime和設計,在此文中作了介紹: The Architecture of Open Source Applications.

速度 - PyPy內建的JIT很棒,有時(其實很少)甚至可以與C相提並論。

GIL問題 - PyPy引入了一個很棒的STM實現,在 Armins Rigo的 文章中對此作了介紹。

粘合程式碼 - 使用cffi可以簡單的處理C庫,甚至比CPython的ctypes還要快!

非同步程式設計。這方面,PyPy內建的 greenlet 比CPython的C擴充套件更適合一些。實際上,非堆疊式的概念(也即greenlet)在PyPy中還在繼續發展(參看https://ep2012.europython.eu/conference/talks/the-story-of-stackless-python)

沙盒技術

應用在web和移動中。這裡有Dusty的一些文章:Pushing Python Past the Present

  PyPy已經支援多平臺 (x86, 64_x86, ARM)

  PyPy同時還包含了一個優秀的現代的架構,在 Jim Huang 的演講 中做了介紹,演講的要點是:

解釋性語言的框架

用於研究和產品的元件組合 (不同的資料模型,垃圾回收 - 這些可以在具體的應用場景進行改變)

構建在基於元件鏈的功能架構之上(翻譯工具鏈)。每一個步驟都會延續/轉換程式模型、引入特徵、各種後端(JVM, JavaScript, LLVM, GCC IR等等)。來看一下翻譯鏈的例子:python 程式碼 -> 位元組碼 -> 函式物件 -> 型別推斷 -> 垃圾收集器 -> JIT

包含大量在架構的不同層次開發的現代的最佳化技術 (這個任務可以簡化)

  相信讓所有軟體支援PyPy需要付出艱鉅的努力 - 需要在現有的庫上做很多工作。不過使用新的工具,編寫支援PyPy和CPython的軟體會比採用C擴充套件的方式更簡單一些(在我們能做什麼一節有介紹)。

CPython遺留問題

  現在來說一下CPython依賴性程式碼的遺留問題 (源於它們緊密依賴 c 擴充套件)。 這些主要是科學相關的軟體 (NumPy, SciPy 等等)。Python在PyPy成為產品級軟體之前早就被用於科學計算(我認為實在2年之前),之後這些軟體在工具、程式碼和社群方面都得到很多發展。這些軟體一起構建了一個很棒的平臺,通常被用來作為Matlab之類的軟體的替換(有人甚至認為是更好的選擇)。要實現這些,C擴充套件是目前唯一的解決方案。現在,這些軟體的發展仍然與CPython緊密繫結,因為要讓科學計算相關的軟體全部支援PyPy,需要大量的工作。近似的方案是使用按需執行的JIT - 對特定的函式做修飾,然後動態的把它們編譯成機器碼並切換到使用c擴充套件。這個想法不需要重寫所有的科學計算的平臺,而速度同樣快速。採用這種方式的典型專案是numba,這是由Continuum Analytics (這家公司主要釋出基於python庫的強壯的科學計算平臺)贊助的。Numba採用這種方式是因為它的快速指令碼需要與其他依賴CPython的科學計算程式碼相容。numba值得學一下,SciPy會議的 numba演講 是一個很好的闡述。

  不得不說Python的科學計算社群非常棒。他們非常關注於質量、易用性和推廣他們的產品(為此組織了很多會議:SciPy大會,PyData等)。感謝他們讓Python成為免費的科學分析平臺的首選。這裡還需要提一下Travis Oliphant,他在社群中付出了很多努力,讓整個平臺協調一致。關於這些可以看一下這篇部落格文章:為何Python是你最終必須學習的程式語言

  PyPy呢?

  我希望PyPy當時並沒有達到產品級可用。

  後續

  關於本文的後續在Reddit上有一個有趣的討論。這是關於在商業上應用PyPy的優缺點的討論。討論者總結了如何使用基於高效能庫的PyPy。最重要的是使用PyPy軟體棧(原始Python、cffi,等等)可以讓維護和最佳化很簡單(例如:延遲計算)。至於缺點,上面提到了,主要與CPython的遺留問題有關。


相關文章