【问题标题】:Cython: Create memoryview without NumPy array?Cython:创建没有 NumPy 数组的内存视图?
【发布时间】:2014-01-27 03:27:33
【问题描述】:

由于我发现内存视图既方便又快捷,我尽量避免在 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

分析告诉我,这里有一些速度可以提高

我能做的是调整函数,因为有时,例如计算这个向量的平均值,有时是总和。所以我可以重写它,求和或取平均值。但是没有办法直接以很少的开销创建内存视图,动态定义大小。类似于 首先创建 ac 缓冲区 使用 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

【问题讨论】:

    标签: python arrays numpy cython memoryview


    【解决方案1】:

    不知道如何处理 cpython 数组,所以我最终通过自制的“内存视图”解决了这个问题,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
    

    【讨论】:

      【解决方案2】:

      您可能会对 Cython 邮件列表中的以下主题感兴趣:

      https://groups.google.com/forum/#!topic/cython-users/CwtU_jYADgM

      看起来有一些不错的选项,如果您可以从函数中返回一个内存视图,该函数在性能不是什么大问题的不同级别上强制执行。

      【讨论】:

      • 读取线程,我要么使用 malloc - 我已经在函数中处理了,但是我不知道在返回指针时如何处理(我不得不修改一些代码) - 或者我使用 cpython.array。这让我去了this thread。还有更多关于如何在 cython 中使用 cpython 数组的信息吗?
      • 请参阅my commentary 了解他们的时间安排和我自己的建议。 (又名。“这些是一些真正具有误导性的基准”)
      【解决方案3】:

      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
      

      将释放内存的函数绑定到内存视图,但是这段代码无法编译。

      【讨论】:

      • callback_memory_free 实际上是cython.view.array 的属性,而不是视图的属性。
      猜你喜欢
      • 1970-01-01
      • 2014-01-25
      • 2017-04-23
      • 2014-11-29
      • 2021-01-21
      • 1970-01-01
      • 1970-01-01
      • 2016-07-15
      • 1970-01-01
      相关资源
      最近更新 更多