因为项目需要,需要优化已有的Python代码。目前Python代码的执行过程是将Python代码转变成一行行指令,然后解释器解释指令的执行,调用到C代码层。如果去掉指令解释这个阶段,直接进入C代码层,效率就比较高了。如果用之前所述的使用Python C API将Python代码改造为C代码并作为Python的内建模块,工作量极其大,也不能保证其正确性,所以这种方法不太现实。而Cython库正好符合这种场景需求,将已有的Python代码转化为C语言的代码,并作为Python的built-in模块扩展。
版本说明:
Python 2.7.13 (CPython)
Cython 0.25.2
Python的文件类型介绍:
.py python的源代码文件
.pyc Python源代码import后,编译生成的字节码
.pyo Python源代码编译优化生成的字节码。pyo比pyc并没有优化多少,只是去掉了断言
.pyd Python的动态链接库(Windows平台)
.py, .pyc, .pyo 运行速度几乎无差别,只是pyc, pyo文件加载的速度更快,不能用文本编辑器查看内容,反编译不太容易
本文的目标是将test.py文件生成test.c文件,然后将test.c文件作为Python源码的一部分,重新编译生成Python,使用时直接import test即可使用test模块。
Cython基本介绍:
文档中这样总结Cython:
Cython is an optimising static compiler for both the Python programming language and the extended Cython programming language (based on Pyrex). It makes writing C extensions for Python as easy as Python itself.
是一个Python编程语言的编译器,写C扩展就像写Python代码一样容易。
其最重要的功能是:
- write Python code that calls back and forth from and to C or C++ code natively at any point.
即 将Python代码翻译为C代码。之后就可以像前面文章介绍的C语言扩展Python模块使用这些C代码了。
Cython基本用法:
在使用Cython编译Python代码时,务必要安装C/C++编译器,本文是直接安装了Visiual Studio 2015的开发环境。
1. 安装Cython库
pip install Cython
就是如此简单明了
2. 编写一个测试代码文件test.py放在D:/test/test.py
def say_hello(): print "hello world"
然后在同一目录下,新建一个setup.py文件,内容如下:
from distutils.core import setup from Cython.Build import cythonize setup(ext_modules = cythonize("test.py"))
cythonize()是Cython提供将Python代码转换成C代码的API,
setup是Python提供的一种发布Python模块的方法。
3. 使用命令行编译Python代码:
python setup.py build_ext --inplace
如果出现这种情况是因为没有C编译器相关的配置没有设置好,在Windows上一般采用Microsoft VisualStudio,不同的VS版本设置不同。
- Visual Studio 2010 (VS10): SET VS90COMNTOOLS=%VS100COMNTOOLS%
- Visual Studio 2012 (VS11): SET VS90COMNTOOLS=%VS110COMNTOOLS%
- Visual Studio 2013 (VS12): SET VS90COMNTOOLS=%VS120COMNTOOLS%
- Visual Studio 2015 (VS14): SET VS90COMNTOOLS=%VS140COMNTOOLS%
- Visual Studio 2017 (VS14): SET VS90COMNTOOLS=%VS150COMNTOOLS%
这里采用VS2015作为C的编译器。
在命令行中输入SET VS90COMNTOOLS=%VS140COMNTOOLS%
然后输入编译命令:python setup.py build_ext --inplace
最终的生成结果如下:
在D:/test/ 目录中:
test.c是test.py转化后的C代码文件,可以看到test.c非常大!!
test.pyd是python的动态链接库,我们在使用import test时会加载
build目录编译过程中生成的临时文件
使用刚刚生成的test模块,就像使用Python的任意模块一样:
这里稍微解释一下 命令行:python setup.py build_ext --inplace
build_ext是指明python生成C/C++的扩展模块(build C/C++ extensions (compile/link to build directory))
--inplace指示 将编译后的扩展模块直接放在与test.py同级的目录中。
整个Cython工作的流程如下图所示:
分两步:
1).py文件使用Cython被编译为.c文件;
2).c文件使用C编译器生成.pyd(windos)或.so(linux)文件。
除了这种普遍的用法外,还可以在Python代码的某些地方加上静态类型声明,也可以更进一步提升Python的运行效率,这些属于小技巧了~
比如:
def say_hello(int s):
cdef int a = 2
print s + 2
s和a变量直接指示为int类型,不用再做动态语言的类型推断了。
小测试:
import math
import time
def f():
time1 = time.time()
for i in range(100000000):
x = math.sqrt(i)
time2 = time.time()
print time2 - time1
这段原生的Python代码运行时间是13.17秒,使用Cython优化后,运行时间为9.36秒。基本上提升30%。其实Cython一般对外声称的效率提升也大概是这么多。