用Cython和PyPy提升Python性能
最近在比較Python和Java的性能。Python在做科學計算方面的性能的確蠻弱的,但網上查閱了一些文檔,發現有很多方法可以優化Python的性能。這裡以一個簡單的積分程序為例,嘗試用幾種方法優化Python性能,並將優化的結果做個比較。
這裡計算的積分如下
未作優化的Python代碼如下
def integrate_f(a, b, N):n s = 0n dx = (b-a) /Nn for i in range(N):n s += 2.71828182846**(-(a+i*dx)**2)n return s*dxnnif __name__ == "__main__":n print integrate_f(1.0,10.0, 100000000)n
在自己的機器上執行這段代碼,耗時46.33s
同樣功能的Java代碼
import java.lang.Math;nnpublic class Test {n public static double integrate_f(double a, double b, int N) {n double s = 0;n double dx = (b-a)/N;n for(int i =0; i <N; i++) {n s += Math.pow(2.71828182846, -Math.pow(a+i*dx, 2));n }n return s*dx;n }n public static void main(String args[]) {n double result = integrate_f(1,10, 100000000);n System.out.println(result);n }n}n
編譯後執行,耗時8.58s
下面用幾種方法優化上面的Python的代碼
1. PyPy
一般說Python都是指CPython解釋器,CPython是廣泛接受的Python標準。PyPy是另一個解釋器,使用了JIT編譯,和CPython高度兼容。不過PyPy的缺點是不支持C擴展模塊,所以如果程序中用到Numpy,Scipy,就沒法用PyPy優化了。
用PyPy執行上面的python程序,耗時8.16s。
2. Numba
Numba是一個加速Python執行的庫,可以用其中的JIT編譯加速代碼的執行。使用Numba JIT的代碼如下
from numba import jitn@jitndef integrate_f(a, b, N):n s = 0n dx = (b-a) /Nn for i in range(N):n s += 2.71828182846**(-(a+i*dx)**2)n return s*dxnnif __name__ == "__main__":n print integrate_f(1.0,10.0, 100000000)n
使用numba的代碼執行耗時14.41s。
3. Cython
Cython將Python代碼編譯成C源碼,再把C源碼轉換成Python擴展模塊。用Cython改寫Python代碼,將動態類型用Cython中的靜態類型聲明後,可以大大提升執行的效率。
不過用Cython優化的步驟有點複雜。需要先生成Python擴展模塊,然後在另外一個程序里import這個模塊並調用模塊中的方法。
Cython改寫的代碼如下:
def integrate_f(double a, double b, int N):n cdef double s = 0n cdef int in cdef double dx = (b-a) /Nn for i in range(N):n s += 2.71828182846**(-(a+i*dx)**2)n return s*dxnnif __name__ == "__main__":n print integrate_f(1,10, 100000000)n
執行Cython優化的代碼,耗時7.36s。
下圖展示了上述優化前和優化後的性能比較:
可以看到,三種優化方式對性能都有很大的提升,其中Cython優化代碼後對性能的提升最大。用Cython優化後的執行時間(7.36s)差不多比未經優化的CPython代碼(46.33s)少了一個數量級,和Java版本(8.58s)的性能相當。
總結
在涉及大量科學計算的項目中,通過優化Python還是有希望達到非常不錯的性能的。Python自帶了很多做科學計算的庫,如Numpy,Scipy等,底層都是用C實現的,性能上已經做了很大的優化。如果需要自己實現演算法,也可以用Cython做優化,將耗費CPU的部分編譯成高效的C代碼來達到性能的提升。
推薦閱讀: