for 循环和算术运算中大型 numpy 数组的内存使用问题

问题描述

我对 python 很陌生。我有一个隐含的时间积分 for 循环,即下一步取决于上一步。基本上,我试图通过利用 numpy 数组的广播功能而不使用更多 for 循环来解决所有不同情况下的时间积分。因此,我有大型数组。

循环运行时间很短(2.5 秒),但在 for 循环内执行操作时,我的 RAM (8 GB) 几乎达到其容量。因此,我正在尝试解决一个内存使用问题。

ndata = 9624
u.shape = (3,9624,11,3,1000)
p.shape = (9624,1,1000)
a1.shape = (11,1000)
a2.shape = (11,1000)
a3.shape = (11,1000)
p_star.shape = (11,1000)
delta_t.shape =(11,1)
k_star.shape =(11,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]) / (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 ufunc 的可选参数 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,out = buff2);
    np.multiply(a3,u[2,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],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,out=buff2)
    np.multiply(2,out = buff3)
    np.divide(gama,buff3,out = buff3)
    np.subtract(1,out = buff3)
    np.multiply(u[2,out = buff3)
    np.multiply(delta_t,out = buff3)
    np.add(buff1,out = u[1,i+1])
    np.add(buff3,i+1])
    #last line goes like that
    

解决方法

问题实际上是数组 p 和 u 的大小。结合起来,它们由大约十亿个浮点数组成,每个浮点数用 64 位表示。或者,总共 8 GB。如果您需要保留所有 9264 个时间步长,恐怕您无能为力——当然,如果您只保留最后两个时间步长,情况就会改变。