通过静态键入更快的代码
注释
此页面使用两种不同的语法变体:
赛马拉松特异性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公司
集成_周期.pyx
定义 (f)(双重的 x):
返回 x ** 2 - x
定义 积分_f(双重的 一, 双重的 b条, 整数 N个):
cdef公司 整数 我
cdef公司 双重的 秒
cdef公司 双重的 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
cdef公司 双重的 (f)(双重的 x) 除了? -2:
返回 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诺吉尔
块只能包含“白色”代码。
注意,Cython根据局部变量的赋值来推导局部变量的类型(包括作为循环变量的目标),也可以减少在任何地方显式指定类型。例如,声明dx公司
没有必要使用上面的double类型,声明的类型秒
在最后一个版本中(其中返回类型属于(f)
已知为C double。)然而,一个显著的例外是算术表达式中使用的整数类型,因为Cython无法确保不会发生溢出(因此返回到对象
万一需要Python的bignum)。若要允许推断C整数类型,请将推断类型
指令到真的
.本指令做类似于汽车
C++中为熟悉的读者提供的关键字具有此语言功能。减少打字的需要会有很大帮助一切,但也会带来惊喜。特别是如果一个人不熟悉c类型的算术表达式。快速概述可以找到在这里.