0x00 为什么需要Cython
- 如果已经阅读过我之前写的 多线程、协程与高并发 博文,或者对CPython解释器、GIL锁有一定了解,那肯定知道Python中的多线程是“假 多线程”,即cpu在同一时刻里只运行一条线程的运算,其他线程处于等待状态。而在C语言中不存在这个问题,因此用C语言和Python语言混合编程,可以很好的将两者的优点相结合。
- 数据的局部性原理:当使用C的时候,更多的数据可以塞进CPU的cache中,因为Python的元素都是Object,而每个Object都是通过字典实现的,cache对这个数据不很友好。并且对于计算机而言,他们对代码的运行速度是越低级的代码运行速度越快,而Python是高级语言,若想要将其提速,那可以将其编译为更为低级的机器码,Cython就是做这样的过程。
- 用Cython可简化python调用c语言程序的繁琐封装过程,提高python代码执行速度
- 在业界主流的几种Python扩展支持C语言的方案(ctypes,swig,cython)中,以c语言程序性能为基准的话,cython封装后下降20%,swig封装后下降70%。功能方面,swig对结构体和回调函数都要使用typemap进行手工编写转换规则,typemap规则写起来略复杂,体验不是很好。cython在结构体和回调上也要进行手工编码处理,不过比较简单。
0x01 安装Cython
重要的事情说一遍:Cython包 != cython包 !!!
在安装Cython前,需先安装Visual Studio Build Tools 和 Windows SDK
源码包安装
wget https://pypi.python.org/packages/b7/67/7e2a817f9e9c773ee3995c1e15204f5d01c8da71882016cac10342ef031b/Cython-0.25.2.tar.gz
tar xzvf Cython-0.25.2.tar.gz
cd Cython-0.25.2
python setup.py install
PIP安装
sudo pip install Cython --install-option="--no-cython-compile"
当看到类似这样的输出,则说明安装成功
C:\Users\87924>cython
Cython (http://cython.org) is a compiler for code written in the
Cython language. Cython is based on Pyrex by Greg Ewing.
Usage: cython [options] sourcefile.{pyx,py} ...
Options:
-V, --version Display version number of cython compiler
-l, --create-listing Write error messages to a listing file
-I, --include-dir <directory> Search for include files in named directory
(multiple include directories are allowed).
-o, --output-file <filename> Specify name of generated C file
-t, --timestamps Only compile newer source files
-f, --force Compile all source files (overrides implied -t)
-v, --verbose Be verbose, print file names on multiple compilation
-p, --embed-positions If specified, the positions in Cython files of each
function definition is embedded in its docstring.
--cleanup <level> Release interned objects on python exit, for memory debugging.
Level indicates aggressiveness, default 0 releases nothing.
-w, --working <directory> Sets the working directory for Cython (the directory modules
are searched from)
--gdb Output debug information for cygdb
--gdb-outdir <directory> Specify gdb debug information output directory. Implies --gdb.
0x02 简单使用
然后,我们按照惯例来一个hello world了解一下大致的编译流程
1. 编写以 .pyx为扩展名的 cython程序
# hello.pyx
print("hello world!")
pyx可以理解为python代码和C代码的连接桥梁。在这里可以将python中的对象转化为C的变量,也可将C的变量转化为Python的对象。
2.编写python程序
# setup.py
from distutils.core import setup
from Cython.Build import cythonize
setup(
ext_modules = cythonize("hello.pyx")
)
这个文件用于编译刚刚写好的.pyx文件拓展,可将其编译封装为可导入的库。
3.执行setup.py编译拓展
python setup.py build_ext --inplace
build_ext参数差不多是编译extension的意思,而 –inplace 参数则是将编译结果输出到当前目录。编译完成后,会生成一个hello.c文件和一个hello.pyd文件(用PyObject* 封装好的库文件,若在Linux下编译则会生成hello.so) 我们需要将这个.pyd或.so文件和python脚本放在同一目录下,以便在Python脚本中使用import命令进行导入。
还看到有些方案是用pyximport模块直接将pyx文件在python脚本中导入,看似省去了编译这一步,实则却是非常危险的行为。如果在导入过程中,因为环境问题导致pyx导入失败,那整个程序就会崩溃,因此我还是赞成手动编译拓展的方法。
4.用python调用拓展
# t.py
import hello
hello.say("world")
运行结果:
PS C:\Users\87924\Desktop\source\_posts> python -u "c:\Users\87924\Desktop\source\_posts\t.py"
Hello world!