gpt4 book ai didi

python - 许多词典使用大量 RAM

转载 作者:太空狗 更新时间:2023-10-30 01:44:17 26 4
gpt4 key购买 nike

我有一个非常简单的 Python 脚本来创建(用于测试目的),一个列表中有 3500 万个字典对象。每个字典对象包含两个键/值对。例如。

{'Name': 'Jordan', 'Age': 35}

该脚本非常简单地查询姓名和年龄,搜索字典列表并返回包含所有匹配字典条目索引的新列表。

然而,正如您在下面看到的,消耗了大量内存。我想我在某个地方犯了一个非常天真的错误。

screenshot of code and task manager show ram usage

我的代码如下:(如果可读性更好,也可以在图片中查看)。

import sys

# Firstly, we will create 35 million records in memory, all will be the same apart from one

def search(key, value, data, age):
print("Searching, please wait")
# Create list to store returned PKs
foundPKS = []
for index in range(0, len(data)):
if key in data[index] and 'Age' in data[index]:
if data[index][key] == value and data[index]['Age'] >= age:
foundPKS.append(index)
results = foundPKS
return results

def createdata():
# Let's create our list for storing our dictionaries
print("Creating database, please wait")
dictList = []
for index in range(0, 35000000):
# Define dictionary
record = {'Name': 'Jordan', 'Age': 25}
if 24500123 <= index <= 24500200:
record['Name'] = 'Chris'
record['Age'] = 33
# Add the dict to a list
dictList.append(record)
return dictList

datareturned = createdata()

keyname = input("For which key do you wish to search?")
valuename = input("Which values do you want to find?")
valueage = input("What is the minimum age?")

print("Full data set object size:" + str(sys.getsizeof(datareturned)))
results = search(keyname, valuename, datareturned, int(valueage))

if len(results) > 0:
print(str(len(results)) + " found. Writing to results.txt")
fo = open("results.txt", "w")
for line in range(0, len(results)):
fo.write(str(results[line]) + "\n")
fo.close()

是什么导致了 RAM 的大量消耗?

最佳答案

dict 对象的开销相当大。这取决于您的 Python 版本和系统架构,但在 Python 3.5 64 位上

In [21]: sys.getsizeof({})
Out[21]: 288

所以猜测:

250*36e6*1e-9 == 9.0

如果我创建了那么多词典,那么这是我的 ram 使用量的下限,以 千兆字节 为单位,而不考虑 list!

与其使用字典作为记录类型,这不是真正的用例,不如使用 namedtuple

为了了解这是如何进行比较的,让我们设置一个等价的元组列表:

In [23]: Record = namedtuple("Record", "name age")

In [24]: records = [Record("john", 28) for _ in range(36000000)]

In [25]: getsizeof = sys.getsizeof

考虑:

In [31]: sum(getsizeof(record)+ getsizeof(record.name) + getsizeof(record.age)  for record in records)
Out[31]: 5220000000

In [32]: _ + getsizeof(records)
Out[32]: 5517842208

In [33]: _ * 1e-9
Out[33]: 5.517842208

所以 5 演出是一个相当保守的上限。例如,它假定没有 small-int 缓存正在进行,这对于 ages 的记录类型来说完全重要。在我自己的系统上,python 进程正在注册 2.7 gigs 的内存使用(通过 top)。

因此,我的机器中实际发生的事情通过对字符串进行保守假设得到更好的建模——平均大小为 10 的唯一字符串,因此没有字符串实习——但对整数自由,假设 int 缓存是为我们处理我们的 int 对象,所以我们只需要担心 8 字节的指针!

In [35]: sum(getsizeof("0123456789") + 8  for record in records)
Out[35]: 2412000000

In [36]: _ + getsizeof(records)
Out[36]: 2709842208

In [37]: _ * 1e-9
Out[37]: 2.709842208

对于我从 top 观察到的内容,这是一个很好的模型。

如果你真的想要高效存储

现在,如果你真的想把数据塞进内存,你将不得不失去 Python 的灵 active 。您可以将 array 模块与 struct 结合使用,以获得类似 C 的内存效率。一个更容易进入的世界可能是 numpy,它允许类似的事情。例如:

In [1]: import numpy as np

In [2]: recordtype = np.dtype([('name', 'S20'),('age', np.uint8)])

In [3]: records = np.empty((36000000), dtype=recordtype)

In [4]: records.nbytes
Out[4]: 756000000

In [5]: records.nbytes*1e-9
Out[5]: 0.756

请注意,我们现在可以非常紧凑。我可以使用 8 位无符号整数(即单个字节)来表示年龄。然而,我立即面临一些不灵活的问题:如果我想要有效地存储字符串,我必须定义一个最大大小。我使用了 'S20',它是 20 个字符。这些是 ASCII 字节,但是 20 个 ascii 字符的字段对于名称可能就足够了。

现在,numpy 为您提供了许多包装 C 编译代码的快速方法。所以,为了玩转它,让我们用一些玩具数据填充我们的记录。姓名将只是简单计数的一串数字,年龄将从均值为 50 且标准差为 10 的正态分布中选择。

In [8]: for i in range(1, 36000000+1):
...: records['name'][i - 1] = b"%08d" % i
...:

In [9]: import random
...: for i in range(36000000):
...: records['age'][i] = max(0, int(random.normalvariate(50, 10)))
...:

现在,我们可以使用 numpy 来查询我们的记录。例如,如果您希望记录的索引给定一些条件,请使用np.where:

In [10]: np.where(records['age'] > 70)
Out[10]: (array([ 58, 146, 192, ..., 35999635, 35999768, 35999927]),)

In [11]: idx = np.where(records['age'] > 70)[0]

In [12]: len(idx)
Out[12]: 643403

因此 643403 记录了年龄 > 70。现在,让我们试试 100:

In [13]: idx = np.where(records['age'] > 100)[0]

In [14]: len(idx)
Out[14]: 9

In [15]: idx
Out[15]:
array([ 2315458, 5088296, 5161049, 7079762, 15574072, 17995993,
25665975, 26724665, 28322943])

In [16]: records[idx]
Out[16]:
array([(b'02315459', 101), (b'05088297', 102), (b'05161050', 101),
(b'07079763', 104), (b'15574073', 101), (b'17995994', 102),
(b'25665976', 101), (b'26724666', 102), (b'28322944', 101)],
dtype=[('name', 'S20'), ('age', 'u1')])

当然,一个主要的不灵 active 是 numpy 数组的大小。调整大小操作是昂贵的。现在,您可以将 numpy.array 包装在某个类中,它将作为一个高效的主干,但在这一点上,您还不如使用一个真正的数据库。幸运的是,Python 带有 sqlite

关于python - 许多词典使用大量 RAM,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43578843/

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