Python的for和while循环是灵活和高级的;它们的语法是自然的,读起来像伪代码。Cython支持for和while循环,无需修改。由于循环在本质上通常占据程序运行时的大部分时间,因此需要记住一些指针,以确保Cython可以将Python循环结构转换为高效的C类似结构。
Cython也会自动将循环索引变量i键入为int, 前提是我们不在循环体中的表达式中使用索引。如果我们确实在表达式中使用i,则Cython无法自动推断该操作是否会溢出,并且保守地拒绝推断C整数类型
下面的示例其实和一个原生Python版本的for循环等效的,传入的变量N和索引i都是动态类型
%%cython
cpdef long long calc():
N=10000000
cdef long long res=0
for i in range(N):res+=i
return res
运行的时间消耗
我们从Cython编译的代码可以知道,原生版本的Python版本的for循环,在Cython编译后的C级别代码,如下图。
也就是在每经历一个循环内部都经历了如下Python内部C级别下的函数检查 PyList_CheckExact →PyList-GET_ITEM (或 PySequence_ITEM)并获取range(N)中当前的索引i,遍历的规模达到一个较大的数量级,效率自然慢
高效的做法是
- 将传入range函数的参数静态指定为C类型的整数
- 为索引变量i静态指定C整数类型
%%cython -a
cpdef long long calc2():
cdef long N=10000000
cdef long i
cdef long long res
for i in range(N):res+=i
return res
运行的时间消耗
由于在执行循环前,对相关的循环变量做静态类型绑定运行的速度提升到原来的13倍,相应地Cython编译器生成的C代码也非常简洁
但如果上面的示例的for in 循环内部涉及到容器(列表,元组,字典等)的循环时,根据情况,类型静态化循环索引变量可能会导致更多开销。若要高效地循环容器,可以考虑使用如下建议
- 将容器转换为等效的C ++容器
- 使用类型化的内存视图
这些方案可能会减少循环开销。我们将在后续了解到更多关于优化循环体的知识
网友评论