優化 Python 性能:PyPy、Numba 與 Cython,誰才是目前最優秀的 Python 運算解決方案?

就算是使用了numpy等包,再做了大量的代碼優化工作,python的運算速度仍然沒法令人滿意。這樣看來,python要擺脫「開發快、運行慢」這種身份還得依靠Cython、Numba和Pypy,還有llvmpy、pyston、PyCUDA等等編譯階段的優化工具。那麼從多快省(通用性、速度、易用性)多角度出發,這些解決方案中誰才是最優秀的?


爪機占坑。看題主關心的應該是科學計算,那麼問題的關鍵在並行化。如果能自動把基於numpy的科學計算代碼編譯然後並行化,那最好不過。用這個眼光來看的話,有幾個解決方案就沒啥意思了。

Numba可以pass掉。它還有一個商業版NumbaPro,能夠自動做多核/GPU優化。反過來說,社區版的Numba短期內也只能編譯到LLVM,不能自動並行化。看題主關心的應該是科學計算,不能並行化的解決方案沒什麼意思。

Cython可以pass掉,手動寫並行,不如直接換C++,用那邊的並行庫。

pypy可以pass掉,這貨拿來做web後端倒挺適合,拿來做科學計算就免了,numpy支持一堆坑。尤其是再做各種第三方numpy優化就更不行了,畢竟它的c api跟CPython都不一樣。

說說容易做並行化的。

開源解決方案里,支持自動並行化的,有numexpr/theano,能把基於numpy的表達式編譯成能在多核/gpu上跑的形式,之前大作業有用到,效果不錯,推薦題主試試。

對於複雜一些的情形,不是幾個numpy表達式能搞掂的計算,你可以手動拆分計算,寫一個worker函數,然後用multiprocessing庫或者IPython Parallel來把worker函數分派到本地/遠程機器上的多個核心搞計算。multiprocessing庫屬於標準庫,在py3k里還有一個標準庫concurrent.futures,相當於一個wrapper,用裡面的ProcessPoolExecutor十分方便。對了,multiprocessing庫不怎麼魯棒,有一系列坑,包括不能在repl下用/不能處理類的成員函數等。按照文檔里最簡單的寫法來就沒問題了。


  1. 答案作廢


推薦pypy,pypy內置jit。

自己編製的不調用外部包的的pure python遺傳演算法代碼,我用python ga.py執行時間為47.6秒,用pypy ga.py執行時間為5.9秒。其實只要代碼寫得好(盡量按pypy官方寫),性能直逼c語言。這個代碼用c語言寫的運行時間是12左右,我估計pypy的jit在代碼編譯優化時做了循環展開,而c語言是直接編譯成二進位優化較少。我把這個代碼轉換寫成c#的,在windows運行時間是20多秒,java沒測試,估計和c#差不多。python寫代碼可讀性強,比較簡短,雖然沒有java/c#/c++工程味強,但比寫c語言要省指針內存的心,適合業務語言系統,如我用於量化交易程序,數據處理程序,演算法原型實現程序,如果不加速的確比較慢,而免費的加速不用白不用,否則是地球二氧化碳排放的罪人。很多別的語言苦苦追求的性能優化,只在你的設計一瞬間,你只要python改成pypy,就可以加速幾倍,體驗一下換個引擎的快感。

pypy的缺點是對很多調用c不支持(numpy官方已經大部分支持),如scikit-learn,對這類不是pure python的代碼盡量抽象出來用進程外調用,或用通信方式。在linux伺服器下可以用mysql 內存表實現個簡單的進程通信實現,也可以用redis來實現通信,這樣支持分散式應用了。如果要簡單用命令行調用返迴文本或資料庫結果也可以。還有傳統的用socket通信,或進程通信,或管道通信。怎麼設計看需求,東西是死的人是活的。這樣分離程序後,可以用pypy加速,又能和python代碼庫兼容了。

Numba加速是在代碼內標註加速,在有的情況下比pypy要快,有的比pypy要慢,其商業版NumbaPro可以支持多線程CUDA顯卡加速。

誰最優秀不要緊,能不能為我所用才是關鍵。

對於很多工程或業務應用,可以先用 Python 語言為新理念和新演算法開發原型設計與迭代,如果證明了該演算法有效,然後可以用 C 或 C++ 重新編寫這一演算法用純手工優化,python加速器可以編寫出高性能代碼,同時還能保持 Python 所提供的高生產率。

所以寫代碼,盡量寫pure python代碼,把不是pure python的庫用介面或設計隔離起來,這樣以後代碼慢了可以加速的方案就比較多了。開始寫python代碼時,把這些不是pure python的部分單獨寫在一個文件封裝好,後面要實現加速時,可以弄個代理代碼來實現介面隔離和加速,這樣寫代碼一開始就不用考慮太多部分,又保留了後續加速升級的空間。當然一開始你要測試一下你用的包能不能支持pypy加速,好到編碼時胸有成竹分開文件。


我用 Cython,挺好用的。

基本只用 typed memoryview 來做與 numpy 的介面,函數內部全部轉成指針,調用 C 函數或者

scipy.linalg.cython_blas + scipy.linalg.cython_lapack


目前主要是用cython 優化, 典型場合的如對文件進行特定的crc. cython的效率提升很明顯.

pypy一直在嘗試, 常常在它發布一個新版本時將項目切換到pypy運行看看, 雖然現在很多使用c擴展的包都能運行, 但這些包對pypy沒有優化, 所以,含有這些包的項目直接遷移到pypy反而比cpython更慢, pypy更適合伺服器環境,很多情況下都需要預熱

Numba沒有在實際項目中嘗試過沒有發言權.


寫庫就用cython,寫應用就用numba,未來是cyjit的!


要速度 ,用Fortran,C,

要簡單,用Matlab,

要簡單,又要免費,用python,

要簡單,又要免費,又要快速,用Julia(非島國AV的julia)。


cython吧,很多Python的高性能包底層都是用cython的來優化的。

numba也可以,不過我覺得cython和numpy這些東西結合得更緊密,而numba雖然有針對性有優化,但是還沒見到有什麼比較成功的實際例子


首先要確認的是你需不需要那麼快?

按我導師的說法一周內能跑完的他都可以接受。優化加速代碼都要消耗精力,而且還會大大減少代碼的抽象性。代碼的可讀性大大下降;再修改難度也大大提高。

其次要確認哪些代碼需要加速?

往往核心計算模塊的加速會大大提高整體程序的運行速度。如果你不清楚哪裡拖慢了你的程序,快取做個profiling吧

第三,你想在哪裡加速?

是在自己的筆記本上加速?還是工作站?抑或是伺服器?還是大型clusters?

各種加速技術的兼容性是不一樣的。比如在你電腦上跑得好好的,但是伺服器上加速包卻安裝錯誤。按我一領導的說法,用C++就可以了。他之前搞的是心臟模擬,後來作為樣板程序在top10的超級計算機上做demo去了。雖然這個極端了點,但他明確地指出了你的代碼對包的依賴性越大出問題的可能性也越大。

我個人的推薦——cython

首先cython的兼容性是非常強的,pip一步搞定。我在AMD,INTEL,IBM的cpu上都用過。十分可靠。其次numpy本身走的就是cython方向,而不是llvm。用cython可以和numpy array更加切合,可以非常精準地控制內存。甚至在極端的情況下,直接寫段c代碼,然後用ctype嫁接到numpy array上也十分簡便。


拋個磚,稍微接觸過一點numba,感覺用llvm做編譯優化做的不錯,比如用下面這段測試:

import os

import numba

@numba.jit()

def c(n):

count=0

for i in xrange(n):

for i in xrange(n):

count+=1

return count

n=99999

c(n)

測試用時1.6s,而如果注釋掉@numba.jit()這句,起碼得跑5分鐘左右!

其他解決方案沒接觸過,不好比較,不過numba個人還是挺喜歡的~

First Steps with numba


如果只涉及到數值計算(物理、工程),numba + numpy其實很好用了,cython雖然也很方便,但是只能寫擴展,而且需要像c那樣定義類型,numba的話只需修飾一下函數就可以,而且速度(當然是純數值計算任務)和cython差不多。

pypy的優勢是基本兼容所有的cpython的語法,但是numpy的支持非常差,主要是一些c擴展在pypy上慢一個數量級,所以在非數值計算領域,pypy是非常好的選擇,基本上能達到c的1/5 ~ 1/3的速度。

當然,改進演算法永遠要比使用什麼更快的語言要更有效。


建議採用用戶最多的,不然出了BUG谷歌不到的體驗可不好受。


C或C++ python.

cython 生成的C代碼醜死了,實際上自己還要優化。C也是很優雅的語言,核心用C寫就行。

numpy 提供了結構體,所以兩種語言間可以傳遞指針。我自己測試的時候發現ctype傳遞參數有長度限制。

科學運算不一定需要混合編程,numpy+scipy 配合MKL,可能比自己寫的C還要快(血的教訓,當時測試不同的混編方案花了3個多星期,結果還沒有直接numpy+mkl快)


Cython + C/C++

Cython 語法和 Python 很相似,開發很快,性能也不錯,比純 Python 要快 5 倍左右。如果還不滿意,可以再把核心代碼用 C/C++ 實現,用 Cython 做連接層,提供友好的 Python 介面。

可以參考 opticspy/lightpipes 項目,核心代碼是 C++ 實現。


最近剛接觸Numba,去年學過一點CUDA,感覺如果計算任務大了還是交給GPU,好一點的GPU,它有幾千個並行單元。


現在科學計算還有另外一種選擇,那就是pytorch。

pytorch主攻神經網路,但是pytorch的tensor部分有幾乎和numpy一樣的API,還可以讀取numpy的array,用pytorch進行cuda運算也是一種選擇。而且pytorch的API設計的很好,很方便。

有的時候簡單方法也很有效,並不需要換runtime,比如矩陣運算的話用Intel mkl支持的numpy比沒有mkl的快很多倍。


pypy發展前景最好,但是cython兼容性最佳


我覺得有一款開源的數學軟體很不錯,持續開發了十多年了,創始人曾是Magma的核心開發者,後來在華盛頓大學成就了他的偉大夢想,轉視頻:

視頻封面SageMath - 地球上最強大的開源數學軟體youku.com視頻

它就是 SageMath Mathematical Software System - Sage。SageMath 集成了眾多 Python 和開源數學庫中的大成者,完整列表見 SageMath Download - upstream。其中不乏 Numpy、Scipy、R、GSL等大牌。


推薦閱讀:

Python 所謂的「閉包」是不是本著故意把人搞暈的態度發明出來的?
學習python為什麼要在linux下?怎麼學?
NumPy和MATLAB哪個強大,Numpy能替代MATLAB嗎?
關於python遞歸的邏輯困惑?
Python出現ValueError: need more than 1 value to unpack 的原因是什麼?

TAG:編程語言 | Python | PyPy | 即時編譯JIT | 性能優化 |