cython,加速python,保护代码(2):Faster code via static typing


http://docs.cython.org/src/quickstart/cythonize.html


Cython is a Python compiler. This means that it can compile normal Python code without changes (with a few obvious exceptions of some as-yet unsupported language features). However, for performance critical code, it is often helpful to add static type declarations, as they will allow Cython to step out of the dynamic nature of the Python code and generate simpler and faster C code - sometimes faster by orders of magnitude.

cython可以完全兼容python,但是显示的声明变量类别对cython有很大帮助:

1)消除python代码的动态特性

2)产生速度更快的c代码


常见的类型声明包括变量声明和函数声明.



原始python代码:

def f(x):
    return x**2-x

def integrate_f(a, b, N):
    s = 0
    dx = (b-a)/N
    for i in range(N):
        s += f(a+i*dx)
    return s * dx
直接编译成cython,有35%的speedup。




声明变量类型:

def f(double x):
    return x**2-x

def integrate_f(double a, double b, int N):
    cdef int i
    cdef double s, dx
    s = 0
    dx = (b-a)/N
    for i in range(N):
        s += f(a+i*dx)
    return s * dx
参与类似于循环这样计算的变量进行声明非常关键,比如 i,a,s,dx。即使 N 和 b 不那么关键,也建议都进行声明。

提速4倍。



函数声明类型:

使用cdef,cpdef

cdef double f(double x) except? -2:
    return x**2-x
150倍提速。

坏处:python编译器不再能识别该函数(不知道怎么调用cdef的函数),python动态类似的特性不再能用(runtime的时候不可能再更改f()函数,固定位double了)。


使用cpdef的区别:Using the cpdef keyword instead of cdef, a Python wrapper is also created, so that the function is available both from Cython (fast, passing typed values directly) and from Python (wrapping values in Python objects). In fact, cpdef does not just provide a Python wrapper, it also installs logic to allow the method to be overridden by python methods, even when called from within cython. This does add a tiny overhead compared to cdef methods.

1)同时生成python wrapper;2)使函数可被重写




决定何时添加类型声明:

Because static typing is often the key to large speed gains, beginners often have a tendency to type everything in sight. This cuts down on both readability and flexibility, and can even slow things down (e.g. by adding unnecessary type checks, conversions, or slow buffer unpacking). On the other hand, it is easy to kill performance by forgetting to type a critical loop variable. Two essential tools to help with this task are profiling and annotation. Profiling should be the first step of any optimization effort, and can tell you where you are spending your time. Cython’s annotation can then tell you why your code is taking time.

Using the -a switch to the cython command line program (or following a link from the Sage notebook) results in an HTML report of Cython code interleaved with the generated C code. Lines are colored according to the level of “typedness” – white lines translate to pure C, while lines that require the Python C-API are yellow (darker as they translate to more C-API interaction). Lines that translate to C code have a plus (+) in front and can be clicked to show the generated code.

This report is invaluable when optimizing a function for speed, and for determining when to release the GIL: in general, a nogil block may contain only “white” code.






下面这个很好:

http://blog.septicmk.com/Python/use-Cython.html

如果需要对python代码添加C/C++的代码,我认为最优先考虑使用的应当是Cython,当然也有其他的方法,比如这篇文章所说。Cython除了能调用原生的C/C++模块外,还可以让你以python的语法来写C/C++的扩展。不过这当中有不少的Tips和Tricks值得注意.

 本文中python==2.7.10, Cython=0.22.1

Cython怎么工作?

首先,Python-C-API是Python解释器中原生的python模块,通过这个API就可以使用C/C++来编写Python扩展。不过为了使Python成功的调用C/C++模块,你得花大量的时间写低级的控制,来包裹原来的代码。而Cython所做的就是通过编译利用Python-C-API帮你完成了这个工作,并最终把它编进一个共享库文件(.so)中。使得你可以在python代码中通过import直接导入进来。

一般来讲,在python中使用C/C++模块两种常见的场景是:

  • 原来的python代码性能太差
  • 有现成的C/C++可供直接调用

首先,你至少得写一个.pyx的文件,写好外部调用的接口函数,然后一个setup.py文件,顺利的话,会生成一个共享库文件(.so),就可以在python代码中import它了.
比如说,如果你要写一个add(x,y)函数,首先:

1
2
3
# add.pyx
def add(x, y):
    return x + y

然后一个编译文件:

1
2
3
4
5
6
7
8
# setup.py
from distutils.core import setup
from Cython.Build import cythonize

setup(
  name = 'add',
  ext_modules = cythonize("add.pyx"),
)

然后编译:

1
python setup.py build_ext --inplace

在你需要调用的地方:

1
2
3
# main.py
import add
print add.add(1, -1)

使用Cython语法

定义变量

使用cdef来定义变量,结构体,常量。

1
2
3
4
5
6
7
cdef int i, *j, k[100]
cdef struct node:
    int key
    float value
cdef enum:
    const = 1
cdef bint flag # bool类型使用bint代替

你可以用一个cdef来把他们写到一起:

1
2
3
4
5
6
7
cdef:
    cdef struct node:
        int 
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值