gpt4 book ai didi

python递归 yield 以减少内存占用

转载 作者:太空宇宙 更新时间:2023-11-04 10:20:31 24 4
gpt4 key购买 nike

我有一个像下面这样的函数,它递归地将一个大数组分成两个子数组,并收集所有子数组以供将来处理。我的问题是有没有办法在拆分过程中产生子数组以减少内存占用,例如split调用的数组很大,~50G。

def split(array, subarrays):
n = len(array)
if n == 1:
return
else:
i = n / 2
subarray1 = array[:i]
subarrays.append(subarray1)
subarray2 = array[i:]
subarrays.append(subarray2)
split(subarray1, subarrays)
split(subarray2, subarrays)
return

subarrays = []
# In production, range(10) will be replaced with a huge array, e.g. 50G
split(range(10), subarrays)
for i in subarrays:
print i
# do some other stuff with each subarray

最佳答案

您可以尝试使用 memoryview , Eli Bendersky 写了一篇不错的 blog entry对此。

不过,我会尝试总结一下。在对象上创建内存 View 时,您正在创建对存储对象的内存中的 (ctype) 数据结构的引用。 memoryview 切片是在该数据结构中查找特定值的位置的引用。您可以在同一底层结构上创建多个 View ,而无需复制任何内容。这就像对列表或数组进行切片一样。

尽管如此,您的数据必须支持缓冲协议(protocol)(numpy 数组和字节数组支持,但列表不支持)。

我觉得加上这一行就够了

memview = memoryview(yourarray)

到您的代码并将其传递给 split 而不是您的数组。

不过要注意两件事:

  • 您正在处理一个大数组,因此对数组的一部分(在一个切片中进行)的更改会传播到覆盖该值的所有其他切片。
  • 您的结果现在是内存 View 对象。要打印它们,您需要先将它们转换(例如转换为列表)。

例子:

>>> memview = memoryview("abcde")
>>> print memview
<memory at 0xfoo>
>>> print list(memview)
['a', 'b', 'c', 'd', 'e']

>>> mv_slice = memview[3:]
>>> print list(mv_slice)
['d', 'e']

>>> mv_slice[0] = 'y'
>>> print list(mv_slice)
['y', 'e']

>>> print list(memview)
['a', 'b', 'c', 'y', 'e']
# note that the change propagated to the main memoryview

当然,所有这些都假设您可以一次将 50GB 加载到内存中。如果你做不到,你应该看看 mmap模块。

编辑 - numpy 字符串数组

Will memoryview work with a numpy array of strings?

seems not. e.g. memview = memoryview(np.array(["abcde", 'aa'])), memview[0] is 'abcde', but memview[1] is 'aa\x00\x00\x00'

好吧,从技术上讲它确实有效。它只是展示了 numpy 如何存储字符串数组。那就是:很糟糕 ;)

如果你像这样创建一个 numpy 字符串数组:

>>> npa = np.array(["abcde", 'aa'])
>>> print repr(npa)
array(['abcde', 'aa'],
dtype='|S5')

您看到 dtype 是 |S5,表示长度为 5 的字符串。较短字符串的“缺失”位置用空(零)字节填充(\x00) (为了方便,numpy 通常对我们隐藏)。这是因为 numpy 使用连续的二维数组将字符串存储在内存中以允许真正快速的随机访问。

这意味着,数组中的所有条目消耗的内存与最长的字符串一样多。
将这个高度构造的数组想象成一个极端的例子:

strings = ["foobar"*100000] + ["f" for _ in xrange(10000)]
huge_npa = np.array(strings, dtype=str)

它包含一个非常长的字符串(600.000 个字符,每个 1 字节)和 10.000 个只有 1 字节的字符串。所以总内存消耗应该在 600KB 左右。如果你创建这个数组,虽然它占用了 6GB 的内存。

Expected:
1 string * 6 bytes * 100.000 => 600.000 * 1 byte = 600 KB
10.000 strings * 1 byte => 10.000 * 1 byte = 10 KB
total 610 kB

Reality:
10.000 strings * 6 bytes * 10.0000 => 6.000.000.000 * 1 byte = 6 GB

如果您的字符串大小差异很大,您可能会在这里浪费大量内存。也许您应该重新考虑为此使用 numpy 数组。

关于python递归 yield 以减少内存占用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32529645/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com