python - 在内存中加载大型字典的巨大内存使用量

标签 python memory python-2.x

我在磁盘上有一个只有 168MB 的文件。它只是一个逗号分隔的单词列表,id。
这个词可以是 1-5 个字符长。有 650 万行。

我在 python 中创建了一个字典来将它加载到内存中,以便我可以根据该单词列表搜索传入的文本。当 python 将它加载到内存中时,它显示使用了 1.3 GB 的 RAM 空间。知道这是为什么吗?

所以假设我的 word 文件看起来像这样......

1,word1
2,word2
3,word3

然后再加上 650 万。
然后我遍历该文件并创建一个字典(python 2.6.1):
def load_term_cache():
    """will load the term cache from our cached file instead of hitting mysql. If it didn't
    preload into memory it would be 20+ million queries per process"""
    global cached_terms
    dumpfile = os.path.join(os.getenv("MY_PATH"), 'datafiles', 'baseterms.txt')
    f = open(dumpfile)
    cache = csv.reader(f)
    for term_id, term in cache:
        cached_terms[term] = term_id
    f.close()

只是这样做会破坏内存。我查看事件监视器,它将内存与所有可用内存 Hook ,最多可达 1.5GB 左右的 RAM 在我的笔记本电脑上,它刚刚开始交换。任何想法如何使用python最有效地将键/值对存储在内存中?

更新:我尝试使用 anydb 模块,在 440 万条记录后它就死了
浮点数是自从我尝试加载它以来经过的秒数

56.95
3400018
60.12
3600019
63.27
3800020
66.43
4000021
69.59
4200022
72.75
4400023
83.42
4600024
168.61
4800025
338.57

你可以看到它运行得很好。每隔几秒插入 200,000 行,直到我撞墙并且时间翻倍。
import anydbm

i=0
mark=0
starttime = time.time()
dbfile = os.path.join(os.getenv("MY_PATH"), 'datafiles', 'baseterms')
db = anydbm.open(dbfile, 'c')
#load from existing baseterm file
termfile = os.path.join(os.getenv("MY_PATH"), 'datafiles', 'baseterms.txt.LARGE')
for line in open(termfile):
    i += 1
    pieces = line.split(',')
    db[str(pieces[1])] = str(pieces[0])
    if i > mark:
        print i
        print round(time.time() - starttime, 2)
        mark = i + 200000
db.close()

最佳答案

很多想法。但是,如果您需要实际帮助,请编辑您的问题以显示您的所有代码。还告诉我们什么是显示已使用内存的“它”,当你加载一个零条目的文件时它显示什么,你在什么平台上,什么版本的 Python。

你说“这个词可以长 1-5 个词”。 BYTES 中关键字段的平均长度是多少? id 都是整数吗?如果是这样,最小和最大整数是多少?如果不是,如果 ID 以字节为单位的平均长度是多少?要启用以上所有内容的交叉检查,您的 6.5M 行文件中有多少字节?

查看您的代码,一个 1 行文件 word1,1 将创建一个 dict d['1'] = 'word1' ......这不是bassackwards吗?

更新 3:更多问题:“单词”是如何编码的?您确定您没有在两个字段中的任何一个上携带大量尾随空格吗?

更新 4 ...您问“ 如何使用 python 最有效地将键/值对存储在内存中”,但没有人给出任何准确的答案。

您有一个 168 Mb 的文件,有 650 万行。那是 168 * 1.024 ** 2/6.5 = 每行 27.1 字节。去掉逗号的 1 个字节和换行符的 1 个字节(假设它是一个 *x 平台),我们每行剩下 25 个字节。假设“id”是唯一的,并且它看起来是一个整数,那么我们假设“id”是 7 个字节长;这使我们的“单词”平均大小为 18 个字节。这符合你的预期吗?

所以,我们想在内存查找表中存储一个 18 字节的键和一个 7 字节的值。

让我们假设一个 32 位 CPython 2.6 平台。

>>> K = sys.getsizeof('123456789012345678')
>>> V = sys.getsizeof('1234567')
>>> K, V
(42, 31)

注意 sys.getsizeof(str_object) => 24 + len(str_object)
一位回答者提到了元组。请仔细注意以下事项:
>>> sys.getsizeof(())
28
>>> sys.getsizeof((1,))
32
>>> sys.getsizeof((1,2))
36
>>> sys.getsizeof((1,2,3))
40
>>> sys.getsizeof(("foo", "bar"))
36
>>> sys.getsizeof(("fooooooooooooooooooooooo", "bar"))
36
>>>

结论:sys.getsizeof(tuple_object) => 28 + 4 * len(tuple_object) ...它只允许指向每个项目的指针,它不允许项目的大小。

对列表的类似分析表明 sys.getsizeof(list_object) => 36 + 4 * len(list_object) ... 再次需要添加项目的大小。还有一个进一步的考虑:CPython 过度分配列表,这样它就不必在每次 list.append() 调用时调用系统 realloc()。对于足够大的大小(比如 650 万!),过度分配是 12.5%——请参阅源代码 (Objects/listobject.c)。这种过度分配不是用元组完成的(它们的大小不会改变)。

以下是基于内存的查找表的 dict 的各种替代方案的成本:

元组列表:

对于 2 元组本身,每个元组将占用 36 个字节,加上内容的 K 和 V。所以他们中的 N 将取 N * (36 + K + V);那么你需要一个列表来保存它们,所以我们需要 36 + 1.125 * 4 * N。

元组列表总数:36 + N * (40.5 + K + v)

那是 26 + 113.5 * N( 大约 709 MB 当是 650 万时)

两个并行列表:

(36 + 1.125 * 4 * N + K * N) + (36 + 1.125 * 4 * N + V * N)
即 72 + N * (9 + K + V)

请注意,当 N 为 650 万时,40.5 * N 和 9 * N 之间的差异约为 200MB。

存储为 int 而不是 str 的值:

但这还不是全部。如果 ID 实际上是整数,我们可以这样存储它们。
>>> sys.getsizeof(1234567)
12

对于每个值对象,这是 12 个字节而不是 31 个字节。当 N 为 650 万时,19 * N 的差异进一步节省了约 118MB。

使用 array.array('l') 而不是列表作为(整数)值:

我们可以将这些 7 位整数存储在 array.array('l') 中。没有 int 对象,也没有指向它们的指针——只是一个 4 字节的有符号整数值。奖励:数组仅被过度分配了 6.25%(对于大 N)。所以这是 1.0625 * 4 * N 而不是之前的 (1.125 * 4 + 12) * N,进一步节省了 12.25 * N,即 76 MB。

所以我们下降到 709 - 200 - 118 - 76 = 大约 315 MB

注意错误和遗漏除外——我的 TZ 是 0127 :-(

关于python - 在内存中加载大型字典的巨大内存使用量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2211965/

相关文章:

python - 如何从 Python 中用户创建的异常类的开头删除 "__main__."

sqlite - 在运行子进程时按 Ctrl+C 终止 Python 程序

python - Django:在下拉列表中显示字典的值

python - 如何在带有 "else"语句的 "for"循环中包含 "if"语句?

java - 内存中有数据的位置但没有数据?

ios - 调用 writeToFile 时防止覆盖 :

c++ - 指针重新分配和多态性

python - 如何检查用户是否将 'input' 或 'raw_input' 提示留空?

python - 分页不接受字典作为数据 - 不可散列的类型

python - 使用 Spacy 计算多个文档相似度的有效方法