4

我有一个返回大型 NumPy 数组的类。这些数组缓存在类中。我希望返回的数组是写时复制数组。如果调用者最终只是从数组中读取,则不会进行任何复制。这将不会使用额外的内存。但是,数组是“可修改的”,但不会修改内部缓存的数组。

我目前的解决方案是将任何缓存数组设置为只读(a.flags.writeable = False)。这意味着如果函数的调用者想要修改数组,他们可能必须制作自己的数组副本。当然,如果源不是来自缓存并且数组已经是可写的,那么他们会不必要地复制数据。

所以,最好我会喜欢类似的东西a.view(flag=copy_on_write)。似乎有一个与之相反的标志,UPDATEIFCOPY这会导致副本在释放后更新原始副本。

谢谢!

4

2 回答 2

5

Copy-on-write 是一个不错的概念,但显式复制似乎是“NumPy 哲学”。因此,如果不太笨拙,我个人会保留“只读”解决方案。

但我承认已经编写了我自己的写时复制包装类。我不尝试检测对数组的写访问。相反,该类有一个方法“get_array(readonly)”返回其(否则为私有的)numpy 数组。第一次使用“readonly=False”调用它时,它会生成一个副本。这是非常明确的,易于阅读和快速理解。

如果您的写时复制 numpy 数组看起来像一个经典的 numpy 数组,那么您的代码的读者(可能是 2 年后的您)可能会遇到困难。

于 2014-02-20T07:07:19.650 回答
3

要实现写入时复制,我们需要修改ndarray 对象的base, data, strides。我认为这不能在纯 Python 代码中完成。我使用一些 Cython 代码来修改这些属性。

这是 IPython 笔记本中的代码:

%load_ext cythonmagic

使用 Cython 定义copy_view()

%%cython
cimport numpy as np

np.import_array()
np.import_ufunc()

def copy_view(np.ndarray a):
    cdef np.ndarray b
    cdef object base
    cdef int i
    base = np.get_array_base(a)
    if base is None or isinstance(base, a.__class__):
        return a
    else:
        print "copy"
        b = a.copy()
        np.set_array_base(a, b)
        a.data = b.data
        for i in range(b.ndim):
            a.strides[i] = b.strides[i]

定义ndarray的子​​类:

class cowarray(np.ndarray):
    def __setitem__(self, key, value):
        copy_view(self)
        np.ndarray.__setitem__(self, key, value)

    def __array_prepare__(self, array, context=None):
        if self is array:
            copy_view(self)
        return array

    def __array__(self):
        copy_view(self)
        return self

一些测试:

a = np.array([1.0, 2, 3, 4])
b = a.view(cowarray)
b[1] = 100 #copy 
print a, b
b[2] = 200 #no copy
print a, b

c = a[::2].view(cowarray)
c[0] = 1000 #copy
print a, c

d = a.view(cowarray)
np.sin(d, d) #copy
print a, d           

输出:

copy
[ 1.  2.  3.  4.] [   1.  100.    3.    4.]
[ 1.  2.  3.  4.] [   1.  100.  200.    4.]
copy
[ 1.  2.  3.  4.] [ 1000.     3.]
copy
[ 1.  2.  3.  4.] [ 0.84147098  0.90929743  0.14112001 -0.7568025 ]
于 2014-02-20T04:04:01.063 回答