【问题标题】:Python hogging memory when looping over list of numpy arrays循环遍历numpy数组列表时Python占用内存
【发布时间】:2023-03-20 01:50:01
【问题描述】:

我正在使用 Python 3 和 Numpy 进行一些科学数据分析,但我遇到了与内存相关的问题。当遍历一个 numpy 数组(其中几千个)列表并进行一些中间计算时,我注意到 python 占用的内存比我预期的要多 6GB。我已将问题隔离为一个函数,如下所示:

def overlap_correct(self):
    running_total = np.zeros((512, 512))
    shutter = 0
    for data_index in range(len(self.data)):
        if self.TOF[data_index] < self.shutter_times[shutter]:
            occupied_prob = running_total/self.N_TRIGS[data_index]
            running_total += self.data[data_index]
            self.data[data_index] = np.round(np.divide(self.data[data_index], (1 - occupied_prob)))
        else:
            running_total = np.zeros((512, 512))
            shutter += 1

这里的相关数据结构是self.data,它是一个包含几千个 512x512 numpy 数组的列表,self.TOFself.N_TRIGS 是几千个浮点数的 numpy 数组,self.shuttertimes 是一个 numpy 数组三个花车。

在这个循环的处理过程中,需要几分钟,我可以观察到 Python 的内存使用量逐渐增加,直到循环结束,内存使用量比开始时多 6GB。

我已经使用memory_profilerobjgraph 来分析内存使用情况,但没有成功。我知道在循环之前和之后,self.dataself.TOFself.N_TRIGSself.shutter 保持相同的大小,并保持相同的数量和相同类型的元素。如果我理解正确的话,occupied _prob 等局部变量应该在 for 循环的每次迭代后超出范围,如果不是,则在函数返回主循环后应该对所有冗余内存进行垃圾回收。这不会发生,并且 6GB 保持锁定直到脚本终止。我也尝试使用gc.collect() 运行手动垃圾收集,但没有任何结果。

如果有帮助,这个函数存在于一个线程中并且是一个更大的数据分析过程的一部分。没有其他线程尝试并发访问数据,并且在线程退出后,self.data 被复制到不同的类。然后通过超出范围来销毁线程的实例。我还尝试使用del thread_instancethread_instance = None 手动销毁线程,但6GB 仍处于锁定状态。这在开发机器上不是一个大问题,但代码将成为更大包的一部分,可以在 RAM 有限的机器上运行。

【问题讨论】:

  • 6GB 似乎适合存放few thousands x 512 x 512 x total bytes per value 大小的数组。或者您的意思是仅将这些结构存储在内存中就比您预期的多 6GB?
  • 你使用的是python 3,那么/np.divide不一样吗? (// 现在是楼层划分?)。我认为您可以执行self.data[data_index] /= 1 - occupied_prob 并保存数组复制步骤。我也怀疑你可以就地取整。
  • @faisal 数据存储在self.data 中,据我所知,已经占用了大约一两个 GB 但仅此而已。问题是在这个函数返回后,大约 6GB MORE 内存被用完了,我无法释放。
  • @AustinHastings 感谢您的想法,少做一个数组复制是个好主意。我不知道就地舍入,只强制 dtype=int 并删除小数点,这对我来说不是一个好的解决方案
  • @AlexanderLiptak 在调用此函数之前 self.data 中的值是多少,是否全为零?这也可以解释here 解释的内存差异。

标签: python loops numpy memory


【解决方案1】:

我已设法找到解决该问题的方法。 TL;DR:在函数执行期间,self.datadtype 未被强制执行。

阻止我意识到这一点的第一个问题是,通过使用sys.getsizeof() 查看self.data 在内存中占用了多少空间,我得到了指向numpy.ndarray 对象的指针列表的大小,这保持不变,因为数组的数量没有改变。

其次,当我检查self.data[0]dtype 时,这是唯一未更改的数据“幻灯片”,我错误地认为整个数组列表也具有相同的dtype

我怀疑某些数组的dtype 发生更改的原因是np.round() 返回一个四舍五入的float

通过将self.data的结构从几千个256x256数组的列表更改为[a few thousand]x[256]x[256]的3D数组,该函数不再猜测数据的dtype,而是默默地转换了返回的float64通过np.rounduint16

self.data = np.asarray(self.data, dtype='uint16')

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-10-26
    • 2019-04-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-15
    • 2016-05-03
    • 1970-01-01
    相关资源
    最近更新 更多