介紹如何安裝與使用 Cython,結合 Python 與 C 語言的優點,提升程式的執行速度。
打開 Windows 的命令提示字元,使用 pip
安裝 cython
套件:
這樣就完成 Cython 的安裝了,不過由於 Cython 需要使用 C/C++ 編譯器,光只有安裝 Cython 是不夠的,必須另外安裝 Visual Studio 2019 才能使用。
從 Visual Studio 的網站下載 Visual Studio 2019 來安裝,個人使用的話下載免費的 Community 版本即可。
由於 Cython 只需要用到 Visual Studio 裡面的 C++建置工具,所以如果沒有用到整個 Visual Studio 的人,可以只下載 Build Tools for Visual Studio 2019 來安裝即可,這樣會省下很多硬碟空間。
以下是 Build Tools for Visual Studio 2019 的安裝步驟。
Step 1
實際安裝前,會先下載安裝程式。
Step 2
只需要選擇左上角的「C++ 建置工具」,其他的原件就不用安裝了。
Step 3
等待下載與安裝過程,跑完之後就完成安裝了。
Cython 可以接受幾乎任何的 Python 程式碼,將其編譯成 C 的擴充模組,以提升執行速度,以下是將普通的 Python 程式碼以 Cython 編譯成擴充模組後,在一般的 Python 程式中引入執行的步驟。
Step 1
這一行是一般 Python 的 Hello World 程式碼:
print("Hello World")
將這一行程式碼儲存成 hello.pyx
。
Step 2
建立一個 Python 指令稿 setup.py
,內容如下:
from distutils.core import setup from Cython.Build import cythonize setup( ext_modules = cythonize("hello.pyx") )
Step 3
建立一個工作目錄,把 hello.pyx
與 setup.py
兩個檔案放在此目錄下:
Step 4
開啟 Windows 的命令提示字元,切換至工作目錄,然後執行以下指令進行編譯:
python setup.py build_ext --inplace
執行完成後,應該會看到「已完成程式碼產生」的訊息,這樣就代表成功編譯完成了。
編譯完成之後,目錄中會產生許多檔案,其中 hello.cp37-win32.pyd
這個檔案就是實際執行時所需要的擴充套件檔(動態連結檔),而其餘的檔案都是編譯過程中產生的暫存檔。
Step 5
有了 hello.cp37-win32.pyd
這個擴充套件檔之後,就可以在一般的 Python 程式中以 import
的方式來引入執行:
import hello
以上是使用 Cython 編譯一般 Python 程式碼的步驟,但是由於這個例子很簡單,看不出 Cython 的效能優勢,要在計算量較大時使用 Cython 才會比較有效益。
這一段 Python 程式碼是用來搜尋質數的函數,我們將示範如何使用 Cython 來加速運算速度,首先將這段程式碼儲存為 primes.py
與 primes_cython.pyx
兩個檔案,兩個內容完全相同。
def find(nb_primes): p = [] n = 2 while len(p) < nb_primes: # 檢查 n 是否為質數 for i in p: if n % i == 0: break # 若是質數,則放進 p else: p.append(n) n += 1 return p
接著編寫 setup.py
指令稿:
from distutils.core import setup from Cython.Build import cythonize setup( ext_modules = cythonize("primes_cython.pyx") )
在 Windows 的命令提示字元中進行編譯:
python setup.py build_ext --inplace
建立一個測試用的 Python 指令稿 run.py
,內容如下:
import primes import primes_cython import time start = time.time() p1 = primes.find(10000) print('Python: %.2f sec' % (time.time() - start)) start = time.time() p2 = primes_cython.find(10000) print('Cython: %.2f sec' % (time.time() - start))
這段測試指令稿可用來比較普通 Python 與 Cython 的執行時間差異。
Python: 9.13 sec Cython: 5.63 sec
上面的質數範例中,我們只使用 Cython 進行編譯,並未修改原來的 Python 程式碼,所以加速並不是很明顯,如果想要獲得更好的效能,可以稍微更改一下程式碼,指定變數的類型,這樣加速的效果會更好。
我們將上面的程式碼修改一下,使用 cdef
指定變數型態後(用法請參考 Cython 官方文件),會變成這樣:
def find(nb_primes): # 以 cdef 指定變數型態 cdef list p = [] cdef unsigned int n = 2 cdef unsigned int i while len(p) < nb_primes: for i in p: if n % i == 0: break else: p.append(n) n += 1 return p
將這段程式碼儲存為 primes_cython2.pyx
,使用 Cython 編譯的過程都一樣。然後在 run.py
中加入新的測試程式碼:
import primes import primes_cython import primes_cython2 import time start = time.time() p1 = primes.find(10000) print('Python: %.2f sec' % (time.time() - start)) start = time.time() p2 = primes_cython.find(10000) print('Cython: %.2f sec' % (time.time() - start)) start = time.time() p2 = primes_cython2.find(10000) print('Cython2: %.2f sec' % (time.time() - start))
Python: 9.72 sec Cython: 5.72 sec Cython2: 0.37 sec
結果非常驚人,執行時間從原本的 9 秒縮短至 0.3 秒。
如果在編譯 *.pyx
檔案時,出現這樣的錯誤:
error: Unable to find vcvarsall.bat
就代表 Cython 找不到編譯器,要安裝 Visual Studio 2019 或是 Build Tools for Visual Studio 2019。
參考資料:賈維斯的智慧工坊、Cython 官方文件