編程派微信號:codingpy
準確的說,Cython是單獨的一門語言,專門用來寫在Python裡面import用的擴展庫。實際上Cython的語法基本上跟Python一致,而Cython有專門的「編譯器」;先將 Cython代碼轉變成C(自動加入了一大堆的C-Python API),然後使用C編譯器編譯出最終的Python可調用的模塊。
本文分享自:http://blog.soliloquize.org/
Cython安裝Cython可以通過pip直接進行安裝,
pip install cython
Cython代碼編譯與使用以一個簡短的代碼樣例來看下Cython如何使用,
定義一個.pyx文件,
import math
def pythagorean(a, b): return math.sqrt(a**2 + b**2)
定義setup.py腳本,
from distutils.core import setup
from Cython.Build import cythonizesetup( ext_modules = cythonize('foo.pyx'),)
在命令行中運行,
python setup.py build_ext --inplace
進入Python命令行,
>>> import foo
>>> foo.pythagorean(3, 4)5.0
在開發調試階段為了簡化操作可以使用Cython中定義的pyximport來直接從.pyx文件加載模塊,
進入Python命令行,
>>> import pyximport
>>> pyximport.install()(None, <pyximport.pyximport.PyxImporter object at 0x026F6C30>)
>>> import foo
>>> foo.pythagorean(3, 4)5.0
使用Cython的目的是為了提升性能,因此需要來實際檢驗一下,擴展foo.pyx增加兩個函數,
import math
def pythagorean(a, b): return math.sqrt(a**2 + b**2)
def fibonacci(n): if n <= 0 or n == 1:
return 1 a = 1 b = 1 for i in xrange(n - 1): c = a + b a = b b = c
return c
def factor(n): if n <= 1:
return 1 return n * factor(n - 1)
這三個函數中pythagorean是簡單的小函數,fibonacci存在循環,factor存在遞歸。純Python代碼可以直接用Cython進行編譯,因此先來看一下在代碼不變的情況下使用Cython會有怎樣的性能變化。
用ipython可以方便的來進行對比,
pip install ipython
在ipython中使用timeit來進行度量,
In [6]: %timeit pythagorean(3, 4)The slowest run took 14.37 times longer than the fastest. This could mean that an intermediate result is being cached.1000000 loops, best of 3: 290 ns per loopIn [7]: %timeit foo.pythagorean(3, 4)The slowest run took 15.36 times longer than the fastest. This could mean that an intermediate result is being cached.1000000 loops, best of 3: 292 ns per loopIn [8]: %timeit fibonacci(100)
100000 loops, best of 3: 8.84 µs per loopIn [9]: %timeit foo.fibonacci(100)
100000 loops, best of 3: 5.11 µs per loopIn [12]: %timeit factor(20)
100000 loops, best of 3: 3.6 µs per loopIn [13]: %timeit foo.factor(20)
100000 loops, best of 3: 2.41 µs per loop
上面三組對比可以看到,在Python腳本代碼不發生改變情況下,將其用Cython進行編譯。如果代碼中存在較多的循環或函數調用,那麼能夠獲得一定的性能提升,但如果代碼本身很精簡,則性能不會有提升,甚至還是Python版本快。
類型信息描述想要獲得更多的性能提升,需要讓Cython知道更多信息。靜態信息越多,Cython能夠獲取到的性能提升也就越多,
cpdef int fibonacci(int n): cdef int i, a, b, c
if n <= 0 or n == 1:
return 1 a = 1 b = 1 for i in xrange(n - 1): c = a + b a = b b = c
return ccpdef long long factor(int n):
if n <= 1:
return 1 return n * factor(n - 1)
將foo.pyx中的代碼做少許修改,增加函數參數、返回值等類型信息,再來看下性能變化,
In [21]: %timeit fibonacci(100)
100000 loops, best of 3: 7.42 µs per loopIn [22]: %timeit foo.fibonacci(100)
100000 loops, best of 3: 4.62 µs per loopIn [26]: %timeit factor(20)
100000 loops, best of 3: 5.83 µs per loopIn [27]: %timeit foo.factor(20)
100000 loops, best of 3: 2.22 µs per loop
可以看到,在提供了類型信息之後,性能提升幅度增大了不少。在這幾個樣例中,能進一步提升的餘地不多了。但一個利好的結論是Cython能夠在對Python代碼進行少量修改的情況下,獲得幅度客觀的性能提升。這種改動甚至不需要對Cython有太深入的了解。
總結從使用者的需求來說,Python代碼面臨性能問題尋求優化時,Cython是其中一個選項。從以上代碼片段來看,Cython使用上算方便,在不對代碼進行大改的情況下可以獲得一定的性能提升。但同樣的,引入Cython也意味著引入了額外的複雜性。最終是否選擇它,還是需要進一步評估其帶來的收益,畢竟也可以看到並不是什麼代碼都能通過它獲得高性能增長的。
參考