【发布时间】:2020-12-21 23:22:00
【问题描述】:
我对python很陌生。我有一个隐式的循环时间积分,即下一步取决于上一步。基本上,我试图通过利用 numpy 数组的广播功能而不使用更多的 for 循环来解决所有不同情况的时间积分。因此,我有很大的数组。
循环运行时间很短(2.5 秒),但在 for 循环中执行操作时,我的 RAM(8 GB)几乎达到了它的容量。因此,我正在尝试解决内存使用问题。
ndata = 9624
u.shape = (3, 9624, 11, 3, 1000)
p.shape = (9624, 11, 1, 1000)
a1.shape = (11, 3, 1000)
a2.shape = (11, 3, 1000)
a3.shape = (11, 3, 1000)
p_star.shape = (11, 3, 1000)
delta_t.shape =(11, 1, 1)
k_star.shape =(11, 3, 1000)
for i in range(0,ndata-1):
p_star = p[i+1] + a1 * u[0,i] + a2 * u[1,i] + a3 * u[2,i]
u[0,i+1] = p_star / k_star
u[1,i+1] = gama * (u[0,i+1]-u[0,i]) / (beta * delta_t) + u[1,i] * (1 - gama/beta) + delta_t * u[2,i] * (1 - gama/(2*beta))
u[2,i+1] = (u[0,i+1]-u[0,i]) / (beta * (delta_t ** 2)) - u[1,i] / (beta * delta_t) - u[2,i] * ((1 / (2*beta)) - 1)
You can see memory usage in the loop here
在算术运算期间,会创建并分配数组的临时副本。我认为这里的主要问题是创建数组的副本。为了克服这个问题,应该就地进行算术运算。
据我所知,要进行这些操作,应该定义 numpy ufuncs 的可选参数 out= 或者应该使用 += -= *= /= 。例如,numpy.sum(a1,a2, out=a1)。
但为了以就地方式执行上述操作,我必须定义许多辅助(或缓冲区)变量。 我的问题是减少这种内存使用的最佳方法是什么。我相信,在 python 中,必须有一种优雅的方式来就地执行这些类型的算术运算。
我也愿意接受其他建议。
buff1 = np.zeros_like(u[0,0])
buff2 = np.zeros_like(u[0,0])
buff3 = np.zeros_like(u[0,0])
buff4 = np.zeros_like(u[0,0])
for i in range(0,ndata-1):
# first line
np.multiply(a1, u[0,i], out = buff1);
np.multiply(a2, u[1,i], out = buff2);
np.multiply(a3, u[2,i], out = buff3);
buff1 += p[i+1];
buff1 += buff2;
buff1 += buff3;
#second line
np.divide(buff1, k_star, out = u[0,i+1])
#third line
np.subtract(u[0,i+1], u[0,i], out = buff1);
buff1 *= gama
buff1 /= beta
buff1 /= delta_t
np.divide(gama,beta, out = buff2)
np.subtract(1, buff2, out=buff2)
np.multiply(u[1,i], buff2, out=buff2)
np.multiply(2, beta, out = buff3)
np.divide(gama, buff3, out = buff3)
np.subtract(1, buff3, out = buff3)
np.multiply(u[2,i], buff3, out = buff3)
np.multiply(delta_t, buff3, out = buff3)
np.add(buff1, buff2, out = u[1,i+1])
np.add(buff3, u[1,i+1], out = u[1,i+1])
#last line goes like that
【问题讨论】:
-
您的
u数组本身就有近十亿个元素。根据 dtype,仅该数组就可以填满您的 RAM。您正在严重挑战硬件的极限。 -
看起来您并不需要一次将所有
u存储在内存中。每个时间步仅依赖于前一个时间步。您不需要同时存储 9624 个时间步长的数据。 -
是的,它非常大,但是在 for 循环 RAM 使用率降低到正常水平(~1-1.5 GB)之后,包括 u 在内的变量会保留。所以这就是为什么我指向循环内的内存使用操作。
-
我将使用 u 中的数据进行进一步计算。不幸的是,需要所有 9624 个时间步
-
有些重要的事情你没有告诉我们。与
u本身相比,循环中创建的任何临时对象都很小。它们远没有大到足以引起你所说的你看到的记忆效应。只有u和p本身大到足以引起问题。
标签: python arrays numpy numpy-ndarray