【问题标题】:Python lists/dictionaries vs. numpy arrays: performance vs. memory controlPython 列表/字典与 numpy 数组:性能与内存控制
【发布时间】:2011-06-23 13:57:37
【问题描述】:

我必须迭代地读取数据文件并将数据存储到 (numpy) 数组中。我选择将数据存储到“数据字段”字典中:{'field1': array1,'field2': array2,...}

案例 1(列表):

使用lists(或collections.deque())“追加”新数据数组,代码高效。但是,当我连接存储在列表中的数组时,内存会增长,我没有设法再次释放它。示例:

filename = 'test'
# data file with a matrix of shape (98, 56)
nFields = 56
# Initialize data dictionary and list of fields
dataDict = {}

# data directory: each entry contains a list 
field_names = []
for i in xrange(nFields):
    field_names.append(repr(i))
    dataDict[repr(i)] = []

# Read a data file N times (it represents N files reading)
# file contains 56 fields of arbitrary length in the example
# Append each time the data fields to the lists (in the data dictionary)
N = 10000
for j in xrange(N):
    xy = np.loadtxt(filename)
    for i,field in enumerate(field_names):
        dataDict[field].append(xy[:,i])

# concatenate list members (arrays) to a numpy array 
for key,value in dataDict.iteritems():
    dataDict[key] = np.concatenate(value,axis=0)

计算时间63.4 s
内存使用率(顶部):13862 gime_se 20 0 1042m 934m 4148 S 0 5.8 1:00.44 蟒蛇

案例 2(numpy 数组):

每次读取时直接连接 numpy 数组,这是低效,但内存仍然受控制 .示例:

nFields = 56
dataDict = {}
# data directory: each entry contains a list 
field_names = []
for i in xrange(nFields):
    field_names.append(repr(i))
    dataDict[repr(i)] = np.array([])

# Read a data file N times (it represents N files reading)
# Concatenate data fields to numpy arrays (in the data dictionary)
N = 10000
for j in xrange(N):
    xy = np.loadtxt(filename)
    for i,field in enumerate(field_names):
        dataDict[field] = np.concatenate((dataDict[field],xy[:,i])) 

计算时间1377.8 s
内存使用率(顶部):14850 gime_se 20 0 650m 542m 4144 S 0 3.4 22:31.21 蟒蛇

问题:

  • 有没有什么方法可以让Case 1的性能,同时又像Case 2一样控制内存?

  • 似乎在情况 1 中,连接列表成员 (np.concatenate(value,axis=0)) 时内存会增长。有更好的想法吗?

【问题讨论】:

  • Numpy 的连接会在您每次使用时创建一个全新的 Numpy 数组。 Numpy 数组的重点是预先分配内存。如果你不这样做,那么你就没有非常明智地使用 Numpy。这就是 Numpy 示例运行缓慢的原因。
  • @Justin:我无法预先分配 numpy 数组,因为我不知道它们之前的长度。这就是为什么我更喜欢使用lists。当我将列表转换为 numpy 数组时出现问题:内存使用量不可挽回地增长。

标签: python performance memory-management


【解决方案1】:

根据我观察到的情况,这是正在发生的事情。确实没有内存泄漏。相反,Python 的内存管理代码(可能与您所在的任何操作系统的内存管理有关)决定在程序中保留原始字典(没有连接数组的字典)使用的空间。但是,它可以免费重复使用。我通过以下方式证明了这一点:

  1. 将您作为答案提供的代码转换为返回 dataDict 的函数。
  2. 两次调用函数并将结果分配给两个不同的变量。

当我这样做时,我发现使用的内存量仅从 ~900 GB 增加到 ~1.3 GB。如果没有额外的字典内存,根据我的计算,Numpy 数据本身应该占用大约 427 MB,所以这加起来。我们的函数创建的第二个初始的、未连接的字典刚刚使用了已分配的内存。

如果您真的不能使用超过 ~600 MB 的内存,那么我建议您使用 Numpy 数组,就像在内部使用 Python 列表所做的那样:分配具有一定数量列的数组,并且当您已经用完了这些,创建一个包含更多列的放大数组并复制数据。这将减少连接的数量,这意味着它会更快(尽管仍然不如列表快),同时保持内存使用的减少。当然,实现起来也比较痛苦。

【讨论】:

  • 我得出了类似的结论,以不同的方式测试代码:使用中间字典。 Python 的内存管理代码(可能与您所在的任何操作系统的内存管理有关)决定在程序中保留原始字典(没有串联数组的字典)使用的空间。 I在 Linux 和 MacOS 上尝试了代码,结果相同。我仍然想知道为什么 python 决定保留已删除字典的空间。可能,这不是内存泄漏,但我仍然看不到它的实际用途。感谢您的建议!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-10-11
  • 2011-02-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-04
  • 1970-01-01
相关资源
最近更新 更多