由于我发现内存视图既方便又快速,所以我尽量避免在 cython 中创建 NumPy 数组并使用给定数组的视图。但是,有时无法避免,不要更改现有数组而是创建一个新数组。在上层函数中这并不明显,但在经常调用的子程序中却很明显。考虑以下函数
#@cython.profile(False)
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.nonecheck(False)
cdef double [:] vec_eq(double [:] v1, int [:] v2, int cond):
''' Function output corresponds to v1[v2 == cond]'''
cdef unsigned int n = v1.shape[0]
cdef unsigned int n_ = 0
# Size of array to create
cdef size_t i
for i in range(n):
if v2[i] == cond:
n_ += 1
# Create array for selection
cdef double [:] s = np.empty(n_, dtype=np_float) # Slow line
# Copy selection to new array
n_ = 0
for i in range(n):
if v2[i] == cond:
s[n_] = v1[i]
n_ += 1
return s
分析告诉我,这里有一些速度
我可以做的是调整函数,因为有时,例如计算这个向量的平均值,有时是总和。所以我可以重写它,求和或取平均值。但是有没有一种方法可以直接以非常小的开销创建内存视图,动态定义大小。像首先使用etc创建 ac 缓冲区,然后在函数结束时将缓冲区转换为 view,传递指针和跨步等等。
malloc
编辑1: 也许对于简单的情况,像这样调整功能是一种可以接受的方法。我只添加了一个参数和求和/取平均值。这样我就不必创建一个数组,并且可以在函数 malloc 中轻松处理。这不会变得更快,不是吗?
# ...
cdef double vec_eq(double [:] v1, int [:] v2, int cond, opt=0):
# additional option argument
''' Function output corresponds to v1[v2 == cond].sum() / .mean()'''
cdef unsigned int n = v1.shape[0]
cdef int n_ = 0
# Size of array to create
cdef Py_ssize_t i
for i in prange(n, nogil=True):
if v2[i] == cond:
n_ += 1
# Create array for selection
cdef double s = 0
cdef double * v3 = <double *> malloc(sizeof(double) * n_)
if v3 == NULL:
abort()
# Copy selection to new array
n_ = 0
for i in range(n):
if v2[i] == cond:
v3[n_] = v1[i]
n_ += 1
# Do further computation here, according to option
# Option 0 for the sum
if opt == 0:
for i in prange(n_, nogil=True):
s += v3[i]
free(v3)
return s
# Option 1 for the mean
else:
for i in prange(n_, nogil=True):
s += v3[i]
free(v3)
return s / n_
# Since in the end there is always only a single double value,
# the memory can be freed right here