Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/cassandra/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么默认dict(int)和#x27;为什么要用这么多的内存?(以及其他简单的python性能问题)_Python_Performance_Memory_Runtime - Fatal编程技术网

为什么默认dict(int)和#x27;为什么要用这么多的内存?(以及其他简单的python性能问题)

为什么默认dict(int)和#x27;为什么要用这么多的内存?(以及其他简单的python性能问题),python,performance,memory,runtime,Python,Performance,Memory,Runtime,我确实理解,以我这样做的方式查询defaultdict中不存在的密钥将向defaultdict添加项。这就是为什么在性能方面比较我的第二个代码段和第一个代码段是公平的 import numpy as num from collections import defaultdict topKeys = range(16384) keys = range(8192) table = dict((k,defaultdict(int)) for k in topKeys) dat = num.zer

我确实理解,以我这样做的方式查询defaultdict中不存在的密钥将向defaultdict添加项。这就是为什么在性能方面比较我的第二个代码段和第一个代码段是公平的

import numpy as num
from collections import defaultdict

topKeys = range(16384)
keys = range(8192)

table = dict((k,defaultdict(int)) for k in topKeys)

dat = num.zeros((16384,8192), dtype="int32")

print "looping begins"
#how much memory should this use? I think it shouldn't use more that a few
#times the memory required to hold (16384*8192) int32's (512 mb), but
#it uses 11 GB!
for k in topKeys:
    for j in keys:
        dat[k,j] = table[k][j]

print "done"
这是怎么回事?此外,与第一个脚本相比,这个类似的脚本需要运行亿万年,并且使用了大量的内存

topKeys = range(16384)
keys = range(8192)
table = [(j,0) for k in topKeys for j in keys]
我猜python整数可能是64位整数,这可以解释其中的一些原因,但是这些相对自然和简单的构造真的会产生如此巨大的开销吗? 我猜这些脚本显示了它们的作用,所以我的问题是:到底是什么导致了第一个脚本中的高内存使用率和第二个脚本的长时间运行和高内存使用率,有没有办法避免这些成本

编辑: 64位机器上的Python 2.6.4

编辑2:我能理解为什么,第一个近似值是,我的表应该占用3GB的空间 16384*8192*(12+12)字节 以及6GB的defaultdict负载因子,强制它保留两倍的空间。 然后,内存分配的低效消耗了另一个2的因素

下面是我剩下的问题: 我有没有办法告诉它以某种方式使用32位整数


为什么我的第二个代码段比第一个要花很长时间运行?第一个大约需要一分钟,我在80分钟后杀死了第二个。

Python int在内部表示为C long(实际上有点复杂),但这并不是问题的根源

最大的开销是使用dicts。(defaultdicts和dicts在本说明中大致相同)。DICT是使用哈希表实现的,这很好,因为它可以快速查找非常通用的键。(当您只需要查找连续的数字键时,就不需要这样做,因为它们可以以一种简单的方式排列以获取它们。)

一个dict可以有比它的项目更多的插槽。假设您有一个dict,其插槽数是项目数的3倍。每个插槽都需要一个指向键的指针和一个用作链接列表结尾的指针。这是数字的6倍,加上所有指向你感兴趣的项目的指针。考虑到这些指针中的每一个都是系统上的8个字节,在这种情况下您有16384个默认值。粗略地看一下,
16384个事件*(8192个项目/事件)*7(指针/项目)*8(字节/指针)=7GB
。这是在我了解您存储的实际数字(每个唯一的数字本身就是一个Python dict)、外部dict、numpy数组或Python跟踪以尝试优化某些内容之前

您的开销听起来比我想象的要高一点,我想知道11GB是用于整个过程还是仅用于表计算。在任何情况下,我都希望这个dict of defaultdicts数据结构的大小比numpy数组表示形式大几个数量级


至于“有没有办法避免这些成本?”答案是“使用numpy来存储大型固定大小的连续数字数组,而不是dict!”你必须更具体、更具体地说明为什么你发现这样一种结构对于更好地建议什么是最佳解决方案是必要的。

好吧,看看你的代码实际上在做什么:

topKeys = range(16384)
table = dict((k,defaultdict(int)) for k in topKeys)
这将创建一个保存16384
defaultdict(int)
的dict。dict有一定的开销:dict对象本身在60到120字节之间(取决于构建中指针和ssize的大小),这就是对象本身;除非dict少于几个项目,否则数据是一个单独的内存块,在12到24字节之间,并且总是在1/2到2/3字节之间填充。DefaultDict要大4到8个字节,因为它们有额外的东西要存储。int每个是12个字节,虽然可以在可能的情况下重用它们,但该代码段不会重用其中的大部分。因此,实际上,在32位构建中,对于
dict,该片段将占用
60+(16384*12)*1.8(填充因子)
字节,
16384*64
字节用于存储为值的默认dict,而
16384*12
字节用于整数。因此,如果不在defaultdicts中存储任何内容,则仅超过1.5兆字节。这是一个32位的构建;64位构建的大小将是该大小的两倍

然后创建一个numpy数组,它实际上对内存非常保守:

dat = num.zeros((16384,8192), dtype="int32")
这会给数组本身带来一些开销,通常的Python对象开销加上数组的维度和类型等等,但不会超过100个字节,而且只对一个数组。但它确实在512Mb内存中存储了
16384*8192
int32

然后你有一种非常奇特的方法来填充这个numpy数组:

for k in topKeys:
    for j in keys:
        dat[k,j] = table[k][j]
这两个循环本身不会占用太多内存,而且每次迭代都会重复使用。但是,
表[k][j]
为您请求的每个值创建一个新的Python整数,并将其存储在defaultdict中。创建的整数始终是
0
,因此它总是被重用,但存储对它的引用仍然会占用defaultdict中的空间:前面提到的每个条目12字节乘以填充因子(介于1.66和2之间),使实际数据接近3Gb,在64位构建中为6Gb

除此之外,由于您不断添加数据,DefaultDict必须不断增长,这意味着它们必须不断重新分配。由于Python的malloc前端(obmalloc),以及它如何在自己的块中分配较小的对象,以及进程内存在大多数操作系统上的工作方式,这意味着您的进程将分配更多,而无法释放它;它实际上不会使用所有11Gb的内存,Python将重用大型块f之间的可用内存
for k in topKeys:
    for j in keys:
        dat[k,j] = table[k][j]
for k in topKeys:
    for j in keys:
        if j in table[k]:
            dat[k,j] = table[k][j]
        else:
            dat[k,j] = 0