【发布时间】: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.TOF 和 self.N_TRIGS 是几千个浮点数的 numpy 数组,self.shuttertimes 是一个 numpy 数组三个花车。
在这个循环的处理过程中,需要几分钟,我可以观察到 Python 的内存使用量逐渐增加,直到循环结束,内存使用量比开始时多 6GB。
我已经使用memory_profiler 和objgraph 来分析内存使用情况,但没有成功。我知道在循环之前和之后,self.data、self.TOF、self.N_TRIGS 和 self.shutter 保持相同的大小,并保持相同的数量和相同类型的元素。如果我理解正确的话,occupied _prob 等局部变量应该在 for 循环的每次迭代后超出范围,如果不是,则在函数返回主循环后应该对所有冗余内存进行垃圾回收。这不会发生,并且 6GB 保持锁定直到脚本终止。我也尝试使用gc.collect() 运行手动垃圾收集,但没有任何结果。
如果有帮助,这个函数存在于一个线程中并且是一个更大的数据分析过程的一部分。没有其他线程尝试并发访问数据,并且在线程退出后,self.data 被复制到不同的类。然后通过超出范围来销毁线程的实例。我还尝试使用del thread_instance 和thread_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 解释的内存差异。