减少python中嵌套循环的执行时间

时间:2018-11-02 19:50:51

标签: python numpy nested-loops

我已经为数字仿真编写了以下简单代码。我的编程水平是初学者。

import numpy as np
import time as t
start = t.time()
r=10**-8
alpha=60*(np.pi/180)
gamma_sa=58.6*10**-3 
gamma_sw=25*10**-3
gamma_pa=153*10**-3
gamma_pw=110*10**-3
gamma_aw=72.5*10**-3    
kt= 1.38*10**-23*293 
i=0
##############variables########################
omega=0 
zeta= (3/2 )*np.pi*r**3 *10**-3
dt=0.01 
std=np.sqrt(2*kt*zeta*dt)
for k in range(1,2):
    beta_i=[]
    j_i=[]
    B=[]
    time=np.arange(dt,10,dt)
    Fs_i=[]
    dE_i=[]
    j=0
    for i in range (len(time)):
        j_i.append(j)
        beta=(90-j)
        beta1=(90-j)*(np.pi/180)
        Fs=0
        Ft = (np.random.randn()*std*np.sqrt(dt))/zeta
        beta_i.append(beta)
        del(beta)
        j=(j+Ft+Fs)%360
    MSD=[]
    diff_i=[]
    tau_i=[]
    for l in range(1,len(time)):
        tau=l*dt
        tau_i.append(tau)
        del(tau)
        for i in range(1,(len(time)-l)):
            diff=(j_i[l+i]-j_i[i])**2*dt
            diff_i.append(diff)
        MSD_j=np.sum(diff_i)/np.max(time)
        MSD.append(MSD_j)
        del(MSD_j) 
    np.savetxt("MSD_no_fs%d"%k, MSD)
    np.savetxt("Tau_no_fs%d"%k,tau_i)
print(t.time() - start)

代码成功运行,执行时间约为38秒。但是,如果我将dt从.01增加到.001,则似乎会花费无限的时间,因为脚本会继续运行而不会出错。有人可以解释关于dt,k范围和时间范围的执行时间依赖性以及执行该操作的任何有效方法吗?因为我想使用dt = .0001,所以krange(0,100,dt)和时间(dt,100,dt)。最佳做法是什么?

PS:8 GB内存和3.31 GHz v6处理器。

1 个答案:

答案 0 :(得分:0)

time的长度与dt的大小成反比。并且在每种情况下,您都有一个基于time长度的嵌套循环。只要该循环是最热门的代码,您的代码就会经历二次增长(O(n**2))。从0.01100.01的步长是(足够接近)100个元素的长度,以及约10,000个单位的内部循环工作。用0.001100.001做同样的事情意味着〜1000个元素和〜1,000,000个工作单元,增加了100倍。

从38秒的起点开始,这非常极端;即使内存不是问题,您也会花费3800秒(超过一个小时)。记忆力是一个问题。您的内部循环会反复从appenddiff_i,因此您将从存储约10,000 float s(在CPython x64构建上似乎占据了24个字节,外加8个字节)。 list中的引用,这意味着您消耗了大约三分之一的RAM。在dt中,0.001的大小最终接近32 MB,而转到0.0001将使您的内存最大为3.2 GB。即使您有那么多的RAM,这也意味着您不再能从CPU高速缓存中获得很多好处,这可能会使您的速度下降甚至超过直接CPU成本所显示的速度。

您的代码对numpy的功能几乎没有优势,并且可能会收紧很多。这样做可以节省大量内存,并允许大多数工作通过C语言执行的循环被推送到单个函数调用中,比Python解释器的运行速度更快。

为获得最简单的改进,请使用diff_i。在循环运行之前,您确切地知道它将有多少个元素,并且可以将计算简化为简单的数组操作,甚至可以在外循环开始之前将j_i转换为numpy数组,然后进行替换在j_i上进行一系列简单的计算,就可以直接将diff_i生成为numpy数组,而根本没有Python级别的循环。

我还没有对此进行测试(在这台机器上没有numpy),但是一个粗略的尝试是将j_i填充后立即将其转换为:

j_i = np.array(j_i)

并将diff_i声明为:

diff_i = np.array()

然后替换:

for i in range(1,(len(time)-l)):
    diff=(j_i[l+i]-j_i[i])**2*dt
    diff_i.append(diff)

具有:

new_diff = (j_i[l+1:] - j_i[:-(l+1)]) ** 2 * dt
diff_i = np.concatenate((diff_i, new_diff))