当前位置: 首页 > 面试题库 >

在内存中加载大字典的内存使用量大

有骏奇
2023-03-14
问题内容

我的磁盘上只有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()

只是这样做会炸毁内存。我查看了活动监视器,它会将内存锁定为所有可用内存,最高可达1.5GB内存。在我的笔记本电脑上,它刚刚开始交换。有什么想法如何使用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'…难道不是吗?

更新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)。元组不会完成这种过度分配(它们的大小不会改变)。

这是基于内存的查找表的各种替代命令的成本:

元组列表:

每个元组将为两个元组本身占用36个字节,并为内容加上K和V。因此,其中N个将取N (36 + K + V);
那么您需要一个列表来保存它们,因此我们需要36 + 1.125 * 4
N。

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

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

两个平行的清单:

(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 not 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 :-(



 类似资料:
  • 问题内容: 我正在尝试将几个文件加载到内存中。这些文件具有以下3种格式之一: 字符串TAB int 字符串TAB浮动 int TAB浮点数。 的确,它们是ngram静态文件,以防解决方案的出现。例如: 目前,我正在执行的伪代码是 令我惊讶的是,尽管磁盘中文件的总大小约为21 mb,但是将其加载到内存中时,该过程将占用120-180 mb的内存!(整个python应用程序不会将其他任何数据加载到内存

  • 我正在尝试在h2o中加载大于内存大小的数据。 H2o博客提到: 下面是连接到h2o 3.6.0.8的代码: 给 我试着把一个169 MB的csv加载到h2o中。 这抛出了一个错误, 这表示内存溢出错误。 问:如果H2opromise加载大于其内存容量的数据集(如上面的博客引述所说的交换到磁盘机制),这是加载数据的正确方法吗?

  • 问题内容: 我正在尝试加载大于h2o中的内存大小的数据。 H2o 博客提到: 这是连接到的代码: 给 我试图将169 MB的csv加载到h2o中。 这引发了错误, 这表示内存不足错误。 问题:如果H2o承诺加载大于其内存容量的数据集(如上面的博客引文所述,交换到磁盘机制),这是加载数据的正确方法吗? 问题答案: 由于性能太差,默认情况下前一会默认禁用“交换到磁盘”。流血边缘(不是最新稳定的)具有启

  • Java1.8。0_131 Windows Server 2012 R2。 “-Xmx=9000m”。但是Windows任务管理器显示java进程使用的内存超过14GB。 NMT显示“内部”消耗超过4.5 GB的内存。为什么会出现这种情况?我知道为本机内存定义空间不是Java功能。但是有什么方法可以限制“内部”内存吗? 总计:保留=15782485KB,提交=14653869KB-Java堆(保留

  • 问题内容: 我正在尝试创建一个文件下载程序作为后台服务,但是当计划了一个大文件时,首先将其放入内存中,然后在下载结束时将文件写入磁盘。 考虑到我可能同时下载许多文件,如何使文件逐渐写入磁盘保留内存? 这是我使用的代码: 问题答案: 我将回调更改为: 这工作得很好。

  • 问题内容: 我是所有内存管理主题的新手,所以有很多我不了解的事情。 我正在尝试将图像缓存在我的应用程序中,但是我在内存消耗方面遇到了麻烦: 所有的Bitmap Chaching代码都可以从此处复制粘贴:http : //developer.android.com/training/displaying- bitmaps/index.html 我调试了代码,并在Eclipse的DDMS视图中检查了堆