【问题标题】:memory leak calling cython function with large numpy array parameters?内存泄漏调用具有大 numpy 数组参数的 cython 函数?
【发布时间】:2015-03-25 04:19:14
【问题描述】:

我正在尝试编写调用以下 cython 函数 test1 的 python 代码,如下所示:

def test1( np.ndarray[np.int32_t, ndim=2] ndk, 
           np.ndarray[np.int32_t, ndim=2] nkw, 
           np.ndarray[np.float64_t, ndim=2] phi):

    for _ in xrange(int(1e5)):
        test2(ndk, nkw, phi)


cdef int test2(np.ndarray[np.int32_t, ndim=2] ndk,
               np.ndarray[np.int32_t, ndim=2] nkw,
               np.ndarray[np.float64_t, ndim=2] phi):
    return 1

我的纯python代码会调用test1并传递3个numpy数组作为参数,它们非常大(大约10^4*10^3)。 test1 将依次调用使用 cdef 关键字定义的 test2 并传递这些数组。由于test1在返回之前需要多次调用test2(大约10^5),并且test2不需要在cython代码之外调用,所以我使用cdef代替def.

但问题是,每次 test1 调用 test2 时,内存开始稳步增加。我试图在这个 cython 代码之外调用gc.collect(),但它不起作用。最后,程序会被系统杀死,因为它已经吃掉了所有的内存。我注意到这个问题只发生在 cdefcpdef 函数中,如果我把它改成 def 就可以了。

我认为 test1 应该将这些数组的引用传递给 test2 而不是对象。但似乎它创建了这些数组的新对象并将它们传递给 test2,并且这些对象之后再也不会被 python gc 触及。

我错过了什么吗?

【问题讨论】:

  • 我无法重现该问题。将np.ones((10^^4, 10^^3), dtype=...) 传递给ndk, nkw, phi 并多次运行test1 可以正常工作。内存似乎没有增加多少。

标签: python numpy cython


【解决方案1】:

我仍然对这个问题感到困惑。但我找到了另一种绕过这个问题的方法。只需明确告诉 cython 像这样传递指针:

def test1( np.ndarray[np.int32_t, ndim=2] ndk, 
           np.ndarray[np.int32_t, ndim=2] nkw, 
           np.ndarray[np.float64_t, ndim=2] phi):

for _ in xrange(int(1e5)):
    test2(&ndk[0,0], &nkw[0,0], &phi[0,0])


cdef int test2(np.int32_t* ndk,
               np.int32_t* nkw,
               np.float64_t* phi):
    return 1

但是,您需要像这样索引数组:ndk[i*row_len + j] 详情:https://github.com/cython/cython/wiki/tutorials-NumpyPointerToC

【讨论】:

    【解决方案2】:

    我遇到了类似的问题,并使用memory views 解决了它。作为解决泄漏的附带好处,与指针相比,此方法使用起来也简单得多:

    类型化内存视图允许高效访问内存缓冲区,例如那些底层 NumPy 数组,而不会产生任何 Python 开销。 Memoryviews 类似于当前的 NumPy 数组缓冲区支持 (np.ndarray[np.float64_t, ndim=2]),但它们具有更多功能和更简洁的语法。

    不幸的是,我无法弄清楚为什么前一种方法会导致内存泄漏 - 我只能猜测指向数据的指针在某处保持活动状态并防止数据被垃圾收集。也许有人可以对此有更好的见解。

    无论如何,您的代码应该可以在此接口上正常工作(例如函数“test2”,但也适用于“test1”):

    cdef int test2(int[:,:] ndk, 
                   int[:,:] nkw, 
                   float[:,:] phi):
    
        # can access data using the referenced memory space, as if it's a regular numpy array 
        # (including properties such as .shape etc. - i.e.:
        # cdef int some_int = ndk[0, 5] <--- return the primitive value stored in [0,5] 
        # ndk.shape <--- will return the shape of the array.
    
        # NOTE: the original array (i.e. ndk which is passed into the function) should 
        # be an "exportable" object, and is presumably created by the caller 
        # (a python/Cython/Numpy array is such an exportable object)
    
        return 1
    

    【讨论】:

      猜你喜欢
      • 2018-03-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多