gpt4 book ai didi

python - 如何在 8G DDR3 RAM 中托管大型列表?

转载 作者:行者123 更新时间:2023-11-28 21:34:28 25 4
gpt4 key购买 nike

我是Python新手,只是想知道内存分配是如何工作的。事实证明,测量存储变量大小的一种方法是使用 sys.getsizeof(x) ,它将返回 x 占用的字节数内存中,对吗?以下是示例代码:

import struct
import sys

x = struct.pack('<L', 0xffffffff)
print(len(x))
print(sys.getsizeof(x))

给出:

4
37

我刚刚创建的变量x是一个4字节的字符串,第一个问题就出现在这里。为什么分配给4字节字符串的内存是37字节?是不是多余的空间太多了?

当我开始创建 2 * 4 字节字符串列表时,情况变得更加复杂。下面你会发现另外几行:

import struct
import sys

k = 2
rng = range(0, k)

x = [b''] * k

for i in rng:
x[i] = struct.pack('<L', 0xffffffff)

print(len(x))
print(len(x[0]))
print(sys.getsizeof(x))
print(sys.getsizeof(x[0]))

从中我得到:

2
4
80
37

另一个问题是,为什么当我在列表中存储两个 4 字节字符串时,分配给它们的内存总和不等于它们单独大小的总和?!即37 + 37 != 80。这额外的 6 个字节有什么用?

让我们将k放大到10000,前面的代码给出:

10000
4
80064
37

将单独大小与整体大小进行比较时,差异显着增大:37 * 10000 = 370000 != 80064。看起来列表中的每个项目现在都占用 80064/10000 = 8.0064 字节。听起来可行,但我仍然无法解决之前显示的冲突。

毕竟,我的主要问题是,当我将 k 提高到 0xffffffff 并期望获得大小为 ~ 8 * 0xffffffff = 34359738360 的列表时 我实际上遇到了MemoryError异常。有什么方法可以消除非关键内存空间,以便我的 8G DDR3 RAM 可以托管这个变量x

最佳答案

Why is the memory allocated to a 4-byte string is 37 bytes? Is not that too much extra space?

Python 中的所有对象在每个对象的基础上都有一定程度的“溢出”。请注意,对于 bytes 以及可能所有不可变的 stdlib 类型,此填充(此处为 33 字节)独立于对象的长度:

from sys import getsizeof as gso
print(gso(b'x'*1000) - gso(b''))
# 1000

请注意,这与:

print(gso([b'x']*1000) - gso(b''))
# 8031

在前者中,您正在创建一个 1000 x 的 bytes 对象。

在后者中,您将创建一个 1000 字节对象的列表。重要的区别在于,在后者中,您 (a) 复制字节对象 1000 次,并合并列表容器的大小。 (差异的原因只是 ~8,000 而不是 ~34,000(即每个元素 8 个字节,而不是每个元素 34 个字节 (=sizeof(b'x')))。)

让我们谈谈容器:

print(gso([b'x'*100,]) - gso([]))

这里我们打印一个元素列表(100 字节长的 byte 对象)和一个空列表的 getsizeof 之间的差异。我们有效地taring超出容器的大小。

我们可能期望这等于getsizeof(b'x' * 100)

事实并非如此。

print(gso([b'x'*100,]) - gso([])) 的结果是 8 个字节(在我的机器上),因为列表只包含引用/指向底层对象的指针,而这 8 个字节就是指向列表中单个元素的指针。

That is 37 + 37 != 80. What are those extra 6 bytes for?

让我们做同样的事情,通过减去容器的大小来查看净大小:

x = [b'\xff\xff\xff\xff', b'\xff\xff\xff\xff']

print(gso(x[0]) - gso(b'')) # 4
print(gso(x) - gso([])) # 16

在第一个例子中,返回的 4 与我提供的第一个示例中返回的 1000 一样,每个字节一个。 (len(x[0]) 为 4)。

在第二个中,每个子列表引用有 8 个字节。它与这些子列表的内容无关:

N = 1000
x = [b'x'] * N
y = [b'xxxx'] * N
print(gso(x) == gso(y))
# True

但是虽然可变容器似乎没有固定的“slop”:

lst = []
for _ in range(100):
lst.append('-')
x = list(lst)

slop = gso(x) - (8 * len(x))
print({"len": len(x), "slop": slop})

输出:

{'len': 1, 'slop': 88}{'len': 2, 'slop': 88}{'len': 3, 'slop': 88}{'len': 4, 'slop': 88}{'len': 5, 'slop': 88}{'len': 6, 'slop': 88}{'len': 7, 'slop': 88}{'len': 8, 'slop': 96}{'len': 9, 'slop': 120}{'len': 10, 'slop': 120}{'len': 11, 'slop': 120}{'len': 12, 'slop': 120}{'len': 13, 'slop': 120}{'len': 14, 'slop': 120}{'len': 15, 'slop': 120}{'len': 16, 'slop': 128}{'len': 17, 'slop': 128}{'len': 18, 'slop': 128}{'len': 19, 'slop': 128}{'len': 20, 'slop': 128}{'len': 21, 'slop': 128}{'len': 22, 'slop': 128}{'len': 23, 'slop': 128}{'len': 24, 'slop': 136}...

...Immutable containers do:

lst = []
for _ in range(100):
lst.append('-')
x = tuple(lst)

slop = gso(x) - (8 * len(x))
print({"len": len(x), "slop": slop})
{'len': 1, 'slop': 48}{'len': 2, 'slop': 48}{'len': 3, 'slop': 48}{'len': 4, 'slop': 48}{'len': 5, 'slop': 48}{'len': 6, 'slop': 48}{'len': 7, 'slop': 48}{'len': 8, 'slop': 48}{'len': 9, 'slop': 48}{'len': 10, 'slop': 48}{'len': 11, 'slop': 48}{'len': 12, 'slop': 48}{'len': 13, 'slop': 48}{'len': 14, 'slop': 48}...

Is there any way to eliminate non-critical memory spaces so that my 8G DDR3 RAM can host this variable x?

First, recall that the sizeof a container will not reflect the entire amount of memory used by Python. The ~8 bytes per element is the size of the pointer, each of those elements will consume an additional 37 (or whatever) bytes (sans interning or similar optimization).

But the good news is that it's unlikely you probably don't need the entire list at the same time. If you're just building a list to iterate over, then generate it one element at a time, with a for loop or generator function.

Or generate it a chunk at a time, process it, and then continue, letting the garbage collector clean up the no-longer-used memory.


One other interesting thing to point out

N = 1000
x = [b'x' for _ in range(N)]
y = [b'x'] * N
print(x == y) # True
print(gso(x) == gso(y)) # False

(这可能是由于 y 的大小是先验已知的,而 x 的大小则未知且已调整大小随着它的成长)。

关于python - 如何在 8G DDR3 RAM 中托管大型列表?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53348835/

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