通过静态键入更快的代码

注释

此页面使用两种不同的语法变体:

  • 赛马拉松特异性cdef公司语法,用于进行类型声明从C/C++的角度来看,简洁且易于阅读。

  • 纯Python语法,允许在纯Python代码,下列的政治公众人物-484类型提示政治公众人物526变量注释。

    要在Python语法中使用C数据类型,您需要导入特殊的赛马要编译的Python模块中的模块,例如。

    进口 赛马
    

    如果您使用纯Python语法,我们强烈建议您使用最新的Cython 3的发布,因为这里进行了重大改进与0.29.x版本相比。

Cython是一个Python编译器。这意味着它可以正常编译没有更改的Python代码(有一些as-yet的明显例外不支持的语言功能,请参阅赛马限制).然而,对于性能关键型代码,添加静态类型声明,因为它们将允许Cython退出Python代码的动态特性并生成更简单更快的C代码-有时速度会快几个数量级。

然而,必须注意,类型声明可以使源代码更冗长,因此可读性较差。因此不鼓励在没有充分理由的情况下使用它们,例如基准测试证明了什么它们确实使代码的性能大大提高临界截面。通常,在正确的位置选择一些类型会有很大帮助。

所有C类型都可用于类型声明:integer和floating点类型、复数、结构、联合和指针类型。Cython可以在上的类型之间自动正确转换转让。这还包括Python的任意大小整数类型,如果转换为C类型时值溢出,将引发Python溢出错误在运行时。(但是,它不会检查溢出做算术时。)生成的C代码将处理在这种情况下,C类型的依赖于平台的大小是正确且安全的。

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

键入变量

考虑以下纯Python代码:

积分.py
定义 (f)(x):
    返回 x ** 2 - x


定义 积分_f(, b条, N个):
     = 0
    dx公司 = (b条 - ) / N个
    对于  在里面 范围(N个):
         += (f)( +  * dx公司)
    返回  * dx公司

简单地在Cython中编译它只会使速度提高35%。这是总比什么都没有要好,但添加一些静态类型可以使差异。

如果使用其他类型声明,则可能如下所示:

集成_周期.py
定义 (f)(x: 赛马.双重的):
    返回 x ** 2 - x


定义 积分_f(: 赛马.双重的, b条: 赛马.双重的, N个: 赛马.整数):
    : 赛马.整数
    : 赛马.双重的
    dx公司: 赛马.双重的
     = 0
    dx公司 = (b条 - ) / N个
    对于  在里面 范围(N个):
         += (f)( +  * dx公司)
    返回  * dx公司

由于迭代器变量使用C语义键入,则将编译for-loop到纯C代码。打字,dx公司因为他们参与其中,所以很重要在for-loop中的算术;打字b条N个减少了不同,但在这种情况下,不需要做太多额外的工作一致并键入整个函数。

这使得速度比纯Python版本提高了4倍。

键入函数

Python函数调用可能非常昂贵——在Cython中,这是因为可能需要在Python对象之间转换以进行调用。在我们上面的例子中,参数被假设为C doublef()在对它的调用中,一只Python浮动对象必须围绕参数以传递它。

因此,Cython提供了一种声明C样式函数的方法,赛马人特有的cdef公司语句,以及@cfunc公司装饰师到用Python语法声明C样式函数。这两种方法都是等效并产生相同的C代码:

@赛马.cfunc公司
@赛马.例外(-2, 检查=真的)
定义 (f)(x: 赛马.双重的) -> 赛马.双重的:
    返回 x ** 2 - x

通常应添加某种形式的异常修饰符,否则应添加Cython将无法传播函数(或它调用的函数)。这个除了? -2表示将检查错误如果-2返回(尽管?表示-2也可以用作有效返回值)。这一点只能用Python来表达修饰器的语法@例外值(-2, check=真).

或者,速度较慢除了 *总是安全。如果函数返回Python,则可以省略except子句对象或确保不会引发异常在函数调用中。同样,Cython提供了装饰器@exceptval(检查=真)提供相同的功能。

的副作用cdef公司(和@cfunc公司decorator)表示函数不再在Python-space中可见,因为Python不知道如何调用它改变的可能性更长f()在运行时。

使用cpdef(cpdef)关键字而不是cdef公司,Python包装器也是创建,以便在Cython(快速,传递直接键入值)和从Python(在Pythons中包装值对象)。事实上,cpdef公司不仅提供了Python包装器,它还安装逻辑以允许方法被python方法覆盖,甚至当从赛顿内部呼叫时。相比之下,这确实增加了一点开销cdef公司方法。同样,Cython提供了@ccall公司提供相同功能的装饰器功能为cpdef(cpdef)关键字。

加速:比纯Python快150倍。

确定添加类型的位置

由于静态键入通常是获得较大速度提升的关键,初学者经常有一种把眼前的一切都打出来的倾向。这两个方面都减少了可读性和灵活性,甚至会降低速度(例如通过添加不必要的类型检查、转换或缓慢的缓冲区解压缩)。另一方面,它很容易被杀死忘记键入关键循环变量的性能。两个基本要素帮助完成此任务的工具是分析和注释。分析应该是任何优化工作的第一步,并且可以告诉你你在哪里消磨时间。Cython的注释可以告诉你为什么你的代码要花时间。

使用-一个切换到赛马命令行程序(或根据Sage笔记本上的链接)生成HTML报告Cython代码与生成的C代码交错。行是根据“类型”的级别进行着色–白线转换为纯C,而需要Python C-API的行是黄色的(当它们转化为更多的C-API交互时,颜色会变暗)。转换为C代码的行有一个加号(+)在前面可以单击以显示生成的代码。

当优化函数以提高速度时,此报告非常宝贵,以及用于确定何时可以释放GIL(请注意,发布GIL仅在有限的情况下有用情况,请参见赛马人和GIL更多详细信息):一般来说,a诺吉尔块只能包含“白色”代码。

../../_图像/htmlreport_py.png

注意,Cython根据局部变量的赋值来推导局部变量的类型(包括作为循环变量的目标),也可以减少在任何地方显式指定类型。例如,声明dx公司没有必要使用上面的double类型,声明的类型在最后一个版本中(其中返回类型属于(f)已知为C double。)然而,一个显著的例外是算术表达式中使用的整数类型,因为Cython无法确保不会发生溢出(因此返回到对象万一需要Python的bignum)。若要允许推断C整数类型,请将推断类型 指令真的.本指令做类似于汽车C++中为熟悉的读者提供的关键字具有此语言功能。减少打字的需要会有很大帮助一切,但也会带来惊喜。特别是如果一个人不熟悉c类型的算术表达式。快速概述可以找到在这里.