6

考虑以下代码:

import numpy as np
a = np.zeros(50)
a[10:20:2] = 1
b = c = a[10:40:4]
print b.flags  # You'll see that b and c are not C_CONTIGUOUS or F_CONTIGUOUS

我的问题:

有没有办法(仅参考b)使两者都b连续c?如果在此操作后np.may_share_memory(b,a)返回完全可以。False

接近但不太成功的事情是: np.ascontiguousarray/np.asfortranarray因为它们将返回一个数组。


我的用例是我有非常大的 3D 字段存储在numpy.ndarray. 为了节省内存,我想将这些字段分解为我真正感兴趣的域部分:

a = a[ix1:ix2,iy1:iy2,iz1:iz2]

对子类的切片比对ndarray对象的切片更受限制,但这应该可以工作并且它会“做正确的事情”——附加在子类上的各种自定义元数据将按预期进行转换/保存。不幸的是,由于这返回 a view, numpy 之后不会释放大数组,所以我实际上并没有在这里保存任何内存。

为了完全清楚,我希望完成两件事:

  • 保留我的类实例上的元数据。切片会起作用,但我不确定其他形式的复制。
  • 使原始数组可以自由地被垃圾收集
4

3 回答 3

6

根据 Alex Martelli 的说法

“确保大量但临时使用内存在完成后将所有资源返回给系统的唯一真正可靠的方法是让这种使用发生在子进程中,该子进程会执行需要内存的工作然后终止。”

但是,以下内容似乎至少释放了一些内存:警告:我测量可用内存的方法是 Linux 特定的:

import time
import numpy as np

def free_memory():
    """
    Return free memory available, including buffer and cached memory
    """
    total = 0
    with open('/proc/meminfo', 'r') as f:
        for line in f:
            line = line.strip()
            if any(line.startswith(field) for field in ('MemFree', 'Buffers', 'Cached')):
                field, amount, unit = line.split()
                amount = int(amount)
                if unit != 'kB':
                    raise ValueError(
                        'Unknown unit {u!r} in /proc/meminfo'.format(u=unit))
                total += amount
    return total

def gen_change_in_memory():
    """
    https://stackoverflow.com/a/14446011/190597 (unutbu)
    """
    f = free_memory()
    diff = 0
    while True:
        yield diff
        f2 = free_memory()
        diff = f - f2
        f = f2
change_in_memory = gen_change_in_memory().next

在分配大数组之前:

print(change_in_memory())
# 0

a = np.zeros(500000)
a[10:20:2] = 1
b = c = a[10:40:4]

分配大数组后:

print(change_in_memory())
# 3844 # KiB

a[:len(b)] = b
b = a[:len(b)]
a.resize(len(b), refcheck=0)
time.sleep(1)

调整大小后可用内存增加:

print(change_in_memory())
# -3708 # KiB
于 2013-03-15T00:59:29.647 回答
3

您可以在 cython 中执行此操作:

In [1]:
%load_ext cythonmagic

In [2]:
%%cython
cimport numpy as np

np.import_array()

def to_c_contiguous(np.ndarray a):
    cdef np.ndarray new
    cdef int dim, i
    new = a.copy()
    dim = np.PyArray_NDIM(new)
    for i in range(dim):
        np.PyArray_STRIDES(a)[i] = np.PyArray_STRIDES(new)[i]
    a.data = new.data
    np.PyArray_UpdateFlags(a, np.NPY_C_CONTIGUOUS)
    np.set_array_base(a, new)

In [8]:
import sys
import numpy as np
a = np.random.rand(10, 10, 10)
b = c = a[::2, 1::3, 2::4]
d = a[::2, 1::3, 2::4]
print sys.getrefcount(a)
to_c_contiguous(b)
print sys.getrefcount(a)
print np.all(b==d)

输出是:

4
3
True

to_c_contiguous(a)将创建 的 c_contiguous 副本a,并将其作为a.

调用后to_c_contiguous(b),a 的 refcount 会减少,当 a 的 refcount 变为 0 时,它会被释放。

于 2013-03-15T02:41:10.540 回答
1

我会声称完成您列出的两件事的正确方法是通过np.copy您创建的切片。

当然,为了使其正常工作,您需要定义一个适当的__array_finalize__. 你不是很清楚为什么你决定首先避免它,但我的感觉是你应该定义它。(你是如何在bx**2不使用的情况下解决问题的__array_finalize__?)

于 2013-03-15T09:10:47.430 回答