Cython的用法以及填坑姿势,快速教程永利澳门游戏网站:

整个快速教程直接上例子,具体对Cython的使用可以看参考文章。以下工作均在Windows 10

因为项目需要,需要优化已有的Python代码。目前Python代码的执行过程是将Python代码转变成一行行指令,然后解释器解释指令的执行,调用到C代码层。如果去掉指令解释这个阶段,直接进入C代码层,效率就比较高了。如果用之前所述的使用Python
C
API将Python代码改造为C代码并作为Python的内建模块,工作量极其大,也不能保证其正确性,所以这种方法不太现实。而Cython库正好符合这种场景需求,将已有的Python代码转化为C语言的代码,并作为Python的built-in模块扩展。

  • Python 2.7 + NumPy 1.11.0 + Cython 0.24 版本上进行。

版本说明:

准备工作

假设现在我们用C实现了一个可以用在数组上的cos函数,函数原型如下:

// 对in_array中的前size个数求cos值,并存放在out_array对应位置上void cos_doubles(double * in_array, double * out_array, int size);

那么总共需要4个文件:

  • cos_doubles.c,C源文件。
  • cos_doubles.h,C头文件。
  • _cos_doubles.pyx,Python的C扩展文件。(注意:之所以前面加个”_”下划线,是因为使用Cython编译打包后会对pyx文件生成同名的c文件,为了避免覆盖掉原来的cos_doubles.c文件,此处加个下划线)
  • setup.py,负责管理编译、打包工作的“配置”脚本。

下面给出4个文件的源代码

Python 2.7.13  (CPython)

cos_doubles.c

#include "cos_doubles.h"#include <math.h>/* Compute the cosine of each element in in_array, storing the result in * out_array. */void cos_doubles(double * in_array, double * out_array, int size){ int i; for(i=0;i<size;i++){ out_array[i] = cos(in_array[i]); }}

Cython 0.25.2

cos_doubles.h

#ifndef _COS_DOUBLES_H#define _COS_DOUBLES_Hvoid cos_doubles(double * in_array, double * out_array, int size);#endif

Python的文件类型介绍:

_cos_doubles.pyx

""" Example of wrapping a C function that takes C double arrays as input using the Numpy declarations from Cython """# import both numpy and the Cython declarations for numpyimport numpy as npcimport numpy as np# if you want to use the Numpy-C-API from Cython# (not strictly necessary for this example)np.import_array()# cdefine the signature of our c functioncdef extern from "cos_doubles.h": void cos_doubles (double * in_array, double * out_array, int size)# create the wrapper code, with numpy type annotationsdef cos_doubles_func(np.ndarray[double, ndim=1, mode="c"] in_array not None, np.ndarray[double, ndim=1, mode="c"] out_array not None): cos_doubles(<double*> np.PyArray_DATA, <double*> np.PyArray_DATA(out_array), in_array.shape[0])

.py       python的源代码文件

setup.py

from distutils.core import setup, Extensionimport numpyfrom Cython.Distutils import build_extsetup( cmdclass={'build_ext': build_ext}, ext_modules=[Extension("cos_doubles", sources=["_cos_doubles.pyx", "cos_doubles.c"], include_dirs=[numpy.get_include

.pyc     Python源代码import后,编译生成的字节码

编译打包

在命令行窗口中进入到上述文件所在同级目录,输入:

>> python setup.py build_ext -i

参数-i表示inplace,即在同级目录下生成Python可调用模块pyd文件。

build过程如下:

永利澳门游戏网站 1build过程

然后可以看见在同级目录下多了两个文件:

  • _cos_doubles.c,使用Python C-API自动包装生成的C文件。
  • cos_doubles.pyx,Python可直接调用的module文件,也就是最终我们所需要的东西。

接下来测试一下:

# file: test.pyimport cos_doublesimport numpy as npimport matplotlib.pyplot as plta = np.linspace(-5, 5, 100)b = np.empty_likecos_doubles.cos_doubles_funcplt.plotplt.show()

运行效果如下图所示:

永利澳门游戏网站 2运行效果

[1] SciPy lecture notes: 2.8. Interfacing with C[2] Working with
NumPy[3] Python中使用C代码:以NumPy为例 [4] Cython学习

.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

永利澳门游戏网站 3

如果出现这种情况是因为没有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

最终的生成结果如下:

永利澳门游戏网站 4

在D:/test/ 目录中:

永利澳门游戏网站 5

test.c是test.py转化后的C代码文件,可以看到test.c非常大!!

test.pyd是python的动态链接库,我们在使用import
test时会加载

build目录编译过程中生成的临时文件

使用刚刚生成的test模块,就像使用Python的任意模块一样:

永利澳门游戏网站 6

 

这里稍微解释一下 命令行:python setup.py build_ext –inplace

build_ext是指明python生成C/C++的扩展模块(build C/C++ extensions
(compile/link to build directory))

–inplace指示 将编译后的扩展模块直接放在与test.py同级的目录中。

 

整个Cython工作的流程如下图所示:

永利澳门游戏网站 7

分两步:

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一般对外声称的效率提升也大概是这么多。

 

Cython中的坑

发表评论

电子邮件地址不会被公开。 必填项已用*标注