Cython安装
Cython可以通过pip直接进行安装,
pip install cython
Cython代码编译与使用
以一个简短的代码样例来看下Cython如何使用,
定义一个.pyx文件,
# foo.pyx
import math
def pythagorean(a, b):
return math.sqrt(a**2 + b**2)
使用disutils、cythonize
定义setup.py脚本,
from distutils.core import setup
from Cython.Build import cythonize
setup(
ext_modules = cythonize('foo.pyx'),
)
在命令行中运行,
python setup.py build_ext --inplace
进入Python命令行,
>>> import foo
>>> foo.pythagorean(3, 4)
5.0
使用pyximport
在开发调试阶段为了简化操作可以使用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 loop
In [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 loop
In [8]: %timeit fibonacci(100)
100000 loops, best of 3: 8.84 µs per loop
In [9]: %timeit foo.fibonacci(100)
100000 loops, best of 3: 5.11 µs per loop
In [12]: %timeit factor(20)
100000 loops, best of 3: 3.6 µs per loop
In [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 c
cpdef 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 loop
In [22]: %timeit foo.fibonacci(100)
100000 loops, best of 3: 4.62 µs per loop
In [26]: %timeit factor(20)
100000 loops, best of 3: 5.83 µs per loop
In [27]: %timeit foo.factor(20)
100000 loops, best of 3: 2.22 µs per loop
可以看到,在提供了类型信息之后,性能提升幅度增大了不少。在这几个样例中,能进一步提升的余地不多了。但一个利好的结论是Cython能够在对Python代码进行少量修改的情况下,获得幅度客观的性能提升。这种改动甚至不需要对Cython有太深入的了解。
总结
从使用者的需求来说,Python代码面临性能问题寻求优化时,Cython是其中一个选项。从以上代码片段来看,Cython使用上算方便,在不对代码进行大改的情况下可以获得一定的性能提升。但同样的,引入Cython也意味着引入了额外的复杂性。最终是否选择它,还是需要进一步评估其带来的收益,毕竟也可以看到并不是什么代码都能通过它获得高性能增长的。