由于我发现内存视图方便快捷,我尝试避免在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
剖析告诉我,这里有一定的速度
http://uppix.com/f-sil252cdaca8001511f6.png
我能做的是调整函数,有时候会导致例如计算这个向量的平均值,有时是总和。所以我可以重写它,用于求和或取平均值。但是没有办法直接创建内存视图,只需很少的开销,动态定义大小。像首先使用malloc
等创建交流缓冲区并在函数末尾将缓冲区转换为视图,传递指针和步幅等等。
编辑1: 也许对于简单的情况,适应功能e。 G。这是一种可接受的方法。我只添加了一个参数并总结/取平均值。这样我就不必创建一个数组,并且可以轻松处理内部函数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
答案 0 :(得分:2)
我不知道如何处理cpython数组,所以我最终通过自制的内存视图&#39;,as proposed by fabrizioM解决了这个问题。我们不会认为这会起作用。在紧凑的循环中创建一个新的np.array非常昂贵,所以这给了我一个显着的加速。由于我只需要一维阵列,所以我甚至不必费心。但即使对于更高维度的阵列,我认为这可能会很顺利。
cdef class Vector:
cdef double *data
cdef public int n_ax0
def __init__(Vector self, int n_ax0):
self.data = <double*> malloc (sizeof(double) * n_ax0)
self.n_ax0 = n_ax0
def __dealloc__(Vector self):
free(self.data)
...
#@cython.profile(False)
@cython.boundscheck(False)
cdef Vector my_vec_func(double [:, ::1] a, int [:] v, int cond, int opt):
# function returning a Vector, which can be hopefully freed by del Vector
cdef int vecsize
cdef size_t i
# defs..
# more stuff...
vecsize = n
cdef Vector v = Vector(vecsize)
for i in range(vecsize):
# computation
v[i] = ...
return v
...
vec = my_vec_func(...
ptr_to_data = vec.data
length_of_vec = vec.n_ax0
答案 1 :(得分:0)
您可能会对Cython邮件列表中的以下主题感兴趣:
https://groups.google.com/forum/#!topic/cython-users/CwtU_jYADgM
看起来有一些不错的选择,如果你可以从你的函数中返回一个内存视图,在一些不同的级别上强制执行,而性能不是那么大的问题。
答案 2 :(得分:0)
从http://docs.cython.org/src/userguide/memoryviews.html开始,可以通过以下方式分配cython内存视图的内存:
cimport cython
cdef type [:] cview = cython.view.array(size = size,
itemsize = sizeof(type), format = "type", allocate_buffer = True)
或
from libc.stdlib import malloc, free
cdef type [:] cview = <type[:size]> malloc(sizeof(type)*size)
两种情况都有效,但首先我有一个问题,如果引入自己的类型(ctypedef some mytype),因为没有合适的格式。 在第二种情况下,存储器的重新分配存在问题。
从手册中,它应该如下工作:
cview.callback_memory_free = free
哪个绑定函数将内存释放到memoryview,但是这段代码不能编译。