Cython官方文档中文翻译:通过静态类型加速代码

  • 说明

    尝试翻译Cython Documentation以助学习。

    水平有限,乐迎指正;文档首页:《Cython官方文档中文翻译

  • 通过静态类型加速代码

    Cython是一个Python编译器。这意味着它可以无损编译普通Python代码(除去有少许尚不支持的语言特性,详见Cython limitations)。然而,对于关键性能代码,增加静态类型声明是一种常见的有效方式,因为他们使Cython脱离Python代码的动态特性,并生成更简单、更快的C代码–有时会快几个数量级。

    类型声明会造成源代码冗余,也就降低了可读性。因此不建议在非必须情况下使用,例如基准测试证明他们确实在性能关键部分显著提速。通常情况下,正确位置的几种类型非常有效。

    可声明所有C类型整型(integer)浮点型(floating point)复数(complex numbers)structsunionspointerCython可以在分配的类型之间自动且正确的转换。也包括Python任意大小的整型(integer),如果在转换成C代码过程中出现溢出错误会在PythonOverflowError错误(但是,在进行算术运算时,并不会检查溢出错误)。在这种情况下,生成的C代码会安全正确的处理好与平台相适应的C类型大小。

    类型,是通过cdef关键字声明的。

  • 定型变量

    考虑下述纯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%。这总比没有好,然是增加一些静态类型会带来更大改变。

    随着额外声明类型,代码如下:

    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语言定型的,此处for循环会被编译成纯C代码。指定a,s,dx类型非常重要,因为他们涉及for循环中的算术运算,对b,N的定型相对就不那么重要了,不过在本例中一致的定型整个函数并不会增加额外工作量。

    加速效果:相比于纯Python有四倍提速。

  • 定型函数

    Python函数调用功耗很大,在Cython中更是翻倍(in Cython doubly),因为需要在Python对象之间进行反复转换。在上节例子中,在f()及调用f()时其参数都假设时C double类型,然而还是需要构造一个Python float对象已备传入。

    因此,Cython提供了一种声明C风格的语法,即cdef关键字:

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

    通常应添加某种形式的except-modifier,否则Cython无法传播该函数(或其调用的函数)中出现的异常。except?-2意思是,如果返回-2,就检查错误(尽管表明-2也可以作为一个有效的返回值)

    或者,慢一点的except *总是安全的。

    如果返回一个Python对象或保证函数内调用不出错,异常子句(except clause)可以忽略(left out)。

    cdef的一个副作用是函数在Python空间就无效了,因为Python不知道如何调用它。运行过程中也不能更改f()

    相比于cdefcpdef关键字会同时创建一个Python封装器,因此函数在Cython(快速,直接传入定型的值)和Python(在Python对象中封装值)中同时有效。事实上,cpdef不仅提供了一个Python封装器,还安装了逻辑,即便是在cython内调用时方法还可被python方法覆盖。与cdef方法相比,这确实增加了一点点开销。

    加速效果:150倍于纯Python

  • 确定添加类型的位置

    因为静态类型添加常常是提速关键,初学者倾向于定型所有内容。这就导致可读性、灵活性双降,甚至会导致降速(比如,添加不必要的类型检查、转换‘缓慢的缓冲区拆包)。另一方面,如果忘记定型一个关键循环变量就会大大拉低表现。针对此问题的两大利器是profiling、annotation:

    • profiling,应是任何优化行为的第一步,可以告诉你哪里(where)拖了速度的后退;
    • Cython’s annotation,则会告诉你代码为何(why)耗时;

    采用-a转到cython命令行程序(Sage notebook择时跟随link),进入Cython代码与生成的C代码相互交错存在的HTML报告。行(lines)根据“typedness”着色:

    • white lines,转成纯C
    • yellow lines,需要Python C-API(需要更多C-API交互,则颜色更深);

    转成C代码的行前面有个“+”,并可点开查看生成的代码。

    当优化函数提速,及确定何时发布GILrelease the GIL)时,此报告非常重要:通常,nogil块可能只有“white”代码。

    在这里插入图片描述

    Cython会根据变量的指配推定局部变量的类型(包括作为循环变量目标),这也可以在任何地方减少显示指定类型的需要。例如,上例中声明dxdouble是不必要的,最后版本的s类型也是(f的返回值类型是C double)。有个例外,计算算术表达式中的整型(integer),由于Cython无法保证不出现溢出(因此如果需要Pythonbignum,则可返回object)。如要允许推定C整型,可将inter_types指令(directive )设置为True。此指令功能类似于C++auto关键字。减少定型所有的需求可能很有用,也可能因此导致意外,特别是对于不熟悉C类型写算术表达式的人。详细的快速教程参见链接

  • References

  1. 原文链接
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值