赛通相当于基于范围?



如何让cython生成这样的C++循环?

int get_sum(const std::vector<MyObject>& my_vect) {
int sum=0;
for(const auto& my_obj : my_vect) {
sum += my_obj.value();
}
return sum;
}

当我尝试使用它生成的代码在 Cython 中编写它时for my_obj in my_vect它会创建一个默认构造的 MyObject,然后迭代my_vect并对该对象进行复制分配。

如何说服 Cython 在 for 循环中使用引用而不是进行复制?

此解决方案似乎提供了您想要的行为:

# dummy.pyx
# cython: language_level = 3
cimport cython
cdef class MyObject:
cdef int value_
def __init__(self, value = 0):
print("Constructor called.")
self.value_ = value
cpdef int value(self):
return self.value_
@cython.boundscheck(False)
cpdef int get_sum_1(MyObject[::1] my_vect):
cdef int sum_ = 0
cdef Py_ssize_t len = my_vect.shape[0]
for i in range(len):
sum_ += (<MyObject>my_vect[i]).value()
return sum_
cpdef int get_sum_2(MyObject[::1] my_vect):
cdef int sum_ = 0
# cdef MyObject obj
for obj in my_vect:
sum_ += obj.value()
return sum_

get_sum_2,上面,使用for obj in my_vect样式(即迭代器(。但是,由于事先不知道obj的类型并且使用了迭代器(迭代范围不是先验已知的(,因此存在大量的Python开销。如果您通过取消注释cdef MyObject obj部分来暗示 Cythonget_sum_2中的obj类型,您将获得大约 30-40% 的加速(请参阅下面的应用程序代码(。

您可以通过使用普通的旧for循环而不是迭代器,在get_sum_2之上获得两个数量级的加速。现在,由于我们提前知道类型化内存视图有多少个元素,因此我们还可以关闭边界检查。

我尝试过的应用程序代码如下:

# app.py
from numpy import array, median
from timeit import repeat
import pyximport
pyximport.install()
from dummy import MyObject
from dummy import get_sum_1
from dummy import get_sum_2

my_vect = array([MyObject(i) for i in range(50000)])
get_1 = repeat("get_sum_1(my_vect)", repeat=100, number=1, globals=globals())
get_2 = repeat("get_sum_2(my_vect)", repeat=100, number=1, globals=globals())
print(f"Median of get_1: {1000*median(get_1)} ms.")
print(f"Median of get_2: {1000*median(get_2)} ms.")

运行python app.py,我收到 50,000 个"构造函数调用"打印语句,然后是笔记本电脑上的性能度量:

Median of get_1: 0.20261999452486634 ms.
Median of get_2: 11.251458498009015 ms.

如果运行cython --annotate dummy.pyx,则应清楚地看到开销。但是,在这两个示例中,除了生成的 C 代码struct MyObject *之外,我看不到任何内容。在相应函数调用的应用程序代码中缺少构造函数的 print 语句,这进一步支持了这一点。

最新更新