为大对象分配名称似乎会大大增加内存使用量
Assigning names to large objects appears to increase memory usage considerably
通常,当我需要调用一个复杂的公式时,我会将它分解成两行或更多行以使代码更易于理解。但是,在分析一些计算 RMSE 的代码时,我发现这样做似乎会增加我的代码的内存使用量。这是一个简化的例子:
import numpy as np
import random
from memory_profiler import profile
@profile
def fun1():
#very large datasets (~750 mb each)
predicted = np.random.rand(100000000)
observed = np.random.rand(100000000)
#calculate residuals as intermediate step
residuals = observed - predicted
#calculate RMSE
RMSE = np.mean(residuals **2) ** 0.5
#delete residuals
del residuals
@profile
def fun2():
#same sized data
predicted = np.random.rand(100000000)
observed = np.random.rand(100000000)
#same calculation, but with residuals and RMSE calculated on same line
RMSE = np.mean((observed - predicted) ** 2) ** 0.5
if __name__ == "__main__":
fun1()
fun2()
输出:
Filename: memtest.py
Line # Mem usage Increment Line Contents
================================================
5 19.9 MiB 0.0 MiB @profile
6 def fun1():
7 782.8 MiB 763.0 MiB predicted = np.random.rand(100000000)
8 1545.8 MiB 762.9 MiB observed = np.random.rand(100000000)
9 2308.8 MiB 763.0 MiB residuals = observed - predicted
10 2308.8 MiB 0.1 MiB RMSE = np.mean(residuals ** 2) ** 0.5
11 1545.9 MiB -762.9 MiB del residuals
Filename: memtest.py
Line # Mem usage Increment Line Contents
================================================
13 20.0 MiB 0.0 MiB @profile
14 def fun2():
15 783.0 MiB 762.9 MiB predicted = np.random.rand(100000000)
16 1545.9 MiB 762.9 MiB observed = np.random.rand(100000000)
17 1545.9 MiB 0.0 MiB RMSE = np.mean((observed - predicted) **
2) ** 0.5
如您所见,第一个函数(计算被拆分)似乎在峰值时需要额外的 ~750 MB - 大概是 residuals
数组的成本。然而,这两个函数都需要创建数组——唯一的区别是第一个函数为其分配了一个名称。这与我对 python 内存管理方式的理解相反。
那么,这是怎么回事?一种想法是,这可能是 memory_profiler 模块的产物。在 运行 期间观察 Windows 任务管理器表明了类似的模式(尽管我知道这不是一个非常值得信赖的验证)。如果这是 "real" 效果,我对内存处理方式的误解是什么?或者,这是特定于 numpy 的吗?
memory_profiler
的 "Mem usage" 列告诉您每行完成后的内存使用情况,而不是该行期间的峰值内存使用情况。在您不保存的版本中 residuals
,该数组在该行完成之前被丢弃,因此它永远不会出现在探查器输出中。
通常,当我需要调用一个复杂的公式时,我会将它分解成两行或更多行以使代码更易于理解。但是,在分析一些计算 RMSE 的代码时,我发现这样做似乎会增加我的代码的内存使用量。这是一个简化的例子:
import numpy as np
import random
from memory_profiler import profile
@profile
def fun1():
#very large datasets (~750 mb each)
predicted = np.random.rand(100000000)
observed = np.random.rand(100000000)
#calculate residuals as intermediate step
residuals = observed - predicted
#calculate RMSE
RMSE = np.mean(residuals **2) ** 0.5
#delete residuals
del residuals
@profile
def fun2():
#same sized data
predicted = np.random.rand(100000000)
observed = np.random.rand(100000000)
#same calculation, but with residuals and RMSE calculated on same line
RMSE = np.mean((observed - predicted) ** 2) ** 0.5
if __name__ == "__main__":
fun1()
fun2()
输出:
Filename: memtest.py
Line # Mem usage Increment Line Contents
================================================
5 19.9 MiB 0.0 MiB @profile
6 def fun1():
7 782.8 MiB 763.0 MiB predicted = np.random.rand(100000000)
8 1545.8 MiB 762.9 MiB observed = np.random.rand(100000000)
9 2308.8 MiB 763.0 MiB residuals = observed - predicted
10 2308.8 MiB 0.1 MiB RMSE = np.mean(residuals ** 2) ** 0.5
11 1545.9 MiB -762.9 MiB del residuals
Filename: memtest.py
Line # Mem usage Increment Line Contents
================================================
13 20.0 MiB 0.0 MiB @profile
14 def fun2():
15 783.0 MiB 762.9 MiB predicted = np.random.rand(100000000)
16 1545.9 MiB 762.9 MiB observed = np.random.rand(100000000)
17 1545.9 MiB 0.0 MiB RMSE = np.mean((observed - predicted) **
2) ** 0.5
如您所见,第一个函数(计算被拆分)似乎在峰值时需要额外的 ~750 MB - 大概是 residuals
数组的成本。然而,这两个函数都需要创建数组——唯一的区别是第一个函数为其分配了一个名称。这与我对 python 内存管理方式的理解相反。
那么,这是怎么回事?一种想法是,这可能是 memory_profiler 模块的产物。在 运行 期间观察 Windows 任务管理器表明了类似的模式(尽管我知道这不是一个非常值得信赖的验证)。如果这是 "real" 效果,我对内存处理方式的误解是什么?或者,这是特定于 numpy 的吗?
memory_profiler
的 "Mem usage" 列告诉您每行完成后的内存使用情况,而不是该行期间的峰值内存使用情况。在您不保存的版本中 residuals
,该数组在该行完成之前被丢弃,因此它永远不会出现在探查器输出中。