美文网首页
Cython系列教程:三. 使用静态类型来加速你的代码

Cython系列教程:三. 使用静态类型来加速你的代码

作者: DeepNLPLearner | 来源:发表于2020-11-30 17:17 被阅读0次

1 前言

上一主题中,我们介绍了如何构建Cython代码,对于一般的python代码,都可以使用cython对其进行编译(当然也有列外,比如一些Cython尚未支持的语言特性https://cython.readthedocs.io/en/latest/src/userguide/limitations.html#cython-limitations),使动态的python代码变成静态的编译语言,从而提高运行效率(一般可以提升30%左右的效率)。然而在一些场景下(e.g.科学计算),你可能希望获得更快的效率,通过添加静态类型声明Cython也能为你办到!正如编译语言中的类型声明一样,Cython中的静态类型声明可以使得python代码脱离python的动态特性,生成更快的C代码,在一些情形下,能够使你的运算获得数十倍的效率提升!

2 变量类型声明

声明类型的方式如下:以cdef作为开头,定义类型与变量名,基本上是c与python的结合吧。

# file: test.pyx
# 声明一个变量
cdef int x
cdef double s, dx

# 声明一堆变量
cdef:
  int x
  double y
  double z

所有C的类型(int float double...)都可以在.pyx文件中声明使用,cython会自动完成python与C之间的类型转换。需要特别指出的是python中的不定长整型在C中运行时,如果发生溢出,则会在运行时报OverflowError的错误,但是在做数学运算时,并不会检查溢出,在这种情况下,生成的C代码将正确、安全地处理平台相关的C类型(这一块我目前还没有理解透彻,期待评论区大神的留言)。
看一个官方例子:
python程序:

# file: integrate_f.py
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:

# file: integrate_f_cy.pyx
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被声明为C类型,因此,该循环体会被转化为纯C代码执行。 a, sdx参与了循环,因此对这三个变量进行类型声明将显著提升效率,而bN则不会对效率产生深远影响。
为了验证一下,可以再在同一文件目录下新建一个python文件test.py。分别导入上述两个文件,对比一下速度,记得integrate_f_cy.pyx需要编译后才能import成功。

3 函数类型声明

之前用def定义的函数本质上还是python的函数,python的函数调用开销是相对较大的,因此在函数调用频繁的场合,可以直接将函数声明为C-style的风格:

cdef double f(double x) except? -2:
    return x ** 2 - x

cdef是声明函数的标记,可以像C一样指定返回值的类型为doubleexcept? -2在这里的意思是:如果函数调用发生了异常,则返回-2,这方便我们追踪bug。如果函数的返回类型是python对象或者你可以保证函数不会出现异常,except也可以不写。
还需要说明的是,cdef声明的函数在编译后是不能在.py文件中调用的,它更像是cython的一个内部函数,对外不可见。如果你想写一个可以在.py中调用的函数,请用cpdf作为函数的声明。

  • 说了这么多,来个实验吧!看看类型声明能够带来多少效率提升。
    同样的函数(从1加到10亿),python原生函数简直奇慢57.4s,简单编译不指定类型,运行结果为35.6s,指定参数类型后,运行时间竟然达到了0.3s!!!


    python.png
    截图20201130185059.png
    截图20201130185656.png

4 后记

天下武功,唯快不破!但鱼与熊掌不可兼而得之。这意味着你想要获得更快的执行效率,你就不得不放弃一些python优美特性:可读性、动态性、高效开发;反之,如果你想要更快地开发你的产品,那么就不要过多地关注于静态类型这些底层维度的东西。客观地讲,Cython的目的不是为了让你全盘皆用Cython开发,如果你是狂热的运行效率追求者,那么就直接上C/C++,这样写出的代码更加稳定高效;如果不是,如无必要,就不要过度依赖Cython而坏了python的味道。

相关文章

网友评论

      本文标题:Cython系列教程:三. 使用静态类型来加速你的代码

      本文链接:https://www.haomeiwen.com/subject/nnlrwktx.html