如何让我的python代码运行得更快

时间:2017-02-22 04:20:48

标签: python performance loops timing

我正在处理循环遍历多个netcdf文件的代码(大~28G)。 netcdf文件在整个域中有多个4D变量[时间,东西,南北,高度]。目标是循环遍历这些文件并循环遍历域中所有这些变量的每个位置,并将某些变量存储到大型数组中。当文件丢失或不完整时,我用99.99填充值。现在我只是通过循环超过2个每日netcdf文件进行测试,但出于某种原因它需要永远(约14小时)。我不确定是否有办法优化此代码。我不认为python应该花费很长时间来执行此任务,但可能是python或我的代码存在问题。下面是我的代码,希望它是可读的,并且非常感谢任何有关如何加快速度的建议:

#Domain to loop over
k_space = np.arange(0,37)
j_space = np.arange(80,170)
i_space = np.arange(200,307)

predictors_wrf=[]
names_wrf=[]

counter = 0
cdate = start_date
while cdate <= end_date:
    if cdate.month not in month_keep:
        cdate+=inc
        continue
    yy = cdate.strftime('%Y')        
    mm = cdate.strftime('%m')
    dd = cdate.strftime('%d')
    filename = wrf_path+'\wrfoutRED_d01_'+yy+'-'+mm+'-'+dd+'_'+hour_str+'_00_00'
    for i in i_space:
        for j in j_space:
            for k in k_space:
                    if os.path.isfile(filename):
                        f = nc.Dataset(filename,'r')
                        times = f.variables['Times'][1:]
                        num_lines = times.shape[0]
                        if num_lines == 144:
                            u = f.variables['U'][1:,k,j,i]
                            v = f.variables['V'][1:,k,j,i]
                            wspd = np.sqrt(u**2.+v**2.)
                            w = f.variables['W'][1:,k,j,i]
                            p = f.variables['P'][1:,k,j,i]
                            t = f.variables['T'][1:,k,j,i]
                        if num_lines < 144:
                            print "partial files for WRF: "+ filename
                            u = np.ones((144,))*99.99
                            v = np.ones((144,))*99.99
                            wspd = np.ones((144,))*99.99
                            w = np.ones((144,))*99.99
                            p = np.ones((144,))*99.99
                            t = np.ones((144,))*99.99
                    else:
                        u = np.ones((144,))*99.99
                        v = np.ones((144,))*99.99
                        wspd = np.ones((144,))*99.99
                        w = np.ones((144,))*99.99
                        p = np.ones((144,))*99.99
                        t = np.ones((144,))*99.99
                        counter=counter+1
                    predictors_wrf.append(u)
                    predictors_wrf.append(v)
                    predictors_wrf.append(wspd)
                    predictors_wrf.append(w)
                    predictors_wrf.append(p)
                    predictors_wrf.append(t)
                    u_names = 'u_'+str(k)+'_'+str(j)+'_'+str(i)
                    v_names = 'v_'+str(k)+'_'+str(j)+'_'+str(i)
                    wspd_names = 'wspd_'+str(k)+'_'+str(j)+'_'+str(i)
                    w_names = 'w_'+str(k)+'_'+str(j)+'_'+str(i)
                    p_names = 'p_'+str(k)+'_'+str(j)+'_'+str(i)
                    t_names = 't_'+str(k)+'_'+str(j)+'_'+str(i)
                    names_wrf.append(u_names)
                    names_wrf.append(v_names)
                    names_wrf.append(wspd_names)
                    names_wrf.append(w_names)
                    names_wrf.append(p_names)
                    names_wrf.append(t_names)
    cdate+=inc

3 个答案:

答案 0 :(得分:2)

这是收紧你的forloop的第一个蹩脚的通行证。由于每个文件只使用一次文件形状,因此可以在循环外移动处理,这样可以减少中断处理中的数据加载量。我仍然无法获得counterinc所做的事情,因为他们似乎没有在循环中更新。您肯定希望查看重复的字符串连接性能,或者将附加到predictors_wrfnames_wrf的性能视为起点

k_space = np.arange(0,37)
j_space = np.arange(80,170)
i_space = np.arange(200,307)

predictors_wrf=[]
names_wrf=[]

counter = 0
cdate = start_date
while cdate <= end_date:
    if cdate.month not in month_keep:
        cdate+=inc
        continue
    yy = cdate.strftime('%Y')        
    mm = cdate.strftime('%m')
    dd = cdate.strftime('%d')
    filename = wrf_path+'\wrfoutRED_d01_'+yy+'-'+mm+'-'+dd+'_'+hour_str+'_00_00'
    file_exists = os.path.isfile(filename)
    if file_exists:
        f = nc.Dataset(filename,'r')
        times = f.variables['Times'][1:]
        num_lines = times.shape[0]
    for i in i_space:
        for j in j_space:
            for k in k_space:
                    if file_exists:    
                        if num_lines == 144:
                            u = f.variables['U'][1:,k,j,i]
                            v = f.variables['V'][1:,k,j,i]
                            wspd = np.sqrt(u**2.+v**2.)
                            w = f.variables['W'][1:,k,j,i]
                            p = f.variables['P'][1:,k,j,i]
                            t = f.variables['T'][1:,k,j,i]
                        if num_lines < 144:
                            print "partial files for WRF: "+ filename
                            u = np.ones((144,))*99.99
                            v = np.ones((144,))*99.99
                            wspd = np.ones((144,))*99.99
                            w = np.ones((144,))*99.99
                            p = np.ones((144,))*99.99
                            t = np.ones((144,))*99.99
                    else:
                        u = np.ones((144,))*99.99
                        v = np.ones((144,))*99.99
                        wspd = np.ones((144,))*99.99
                        w = np.ones((144,))*99.99
                        p = np.ones((144,))*99.99
                        t = np.ones((144,))*99.99
                        counter=counter+1
                    predictors_wrf.append(u)
                    predictors_wrf.append(v)
                    predictors_wrf.append(wspd)
                    predictors_wrf.append(w)
                    predictors_wrf.append(p)
                    predictors_wrf.append(t)
                    u_names = 'u_'+str(k)+'_'+str(j)+'_'+str(i)
                    v_names = 'v_'+str(k)+'_'+str(j)+'_'+str(i)
                    wspd_names = 'wspd_'+str(k)+'_'+str(j)+'_'+str(i)
                    w_names = 'w_'+str(k)+'_'+str(j)+'_'+str(i)
                    p_names = 'p_'+str(k)+'_'+str(j)+'_'+str(i)
                    t_names = 't_'+str(k)+'_'+str(j)+'_'+str(i)
                    names_wrf.append(u_names)
                    names_wrf.append(v_names)
                    names_wrf.append(wspd_names)
                    names_wrf.append(w_names)
                    names_wrf.append(p_names)
                    names_wrf.append(t_names)
    cdate+=inc

答案 1 :(得分:2)

对于您的问题,我认为multiprocessing会有很大帮助。我仔细检查了你的代码,并在这里提出了一些建议。

  1. 不使用开始时间,但文件名作为代码中的迭代器。

    包装函数以根据时间找出所有文件名并返回所有文件名列表。

    def fileNames(start_date, end_date):
        # Find all filenames.
        cdate = start_date
        fileNameList = [] 
        while cdate <= end_date:
            if cdate.month not in month_keep:
                cdate+=inc
                continue
            yy = cdate.strftime('%Y')        
            mm = cdate.strftime('%m')
            dd = cdate.strftime('%d')
            filename = wrf_path+'\wrfoutRED_d01_'+yy+'-'+mm+'-'+dd+'_'+hour_str+'_00_00'
            fileNameList.append(filename)
            cdate+=inc
    
        return fileNameList
    
  2. 包裹您的数据以填充数据并填入99.99,该函数的输入是文件名。

    def dataExtraction(filename):
        file_exists = os.path.isfile(filename)
        if file_exists:
           f = nc.Dataset(filename,'r')
           times = f.variables['Times'][1:]
           num_lines = times.shape[0]
        for i in i_space:
            for j in j_space:
                for k in k_space:
                    if file_exists:    
                        if num_lines == 144:
                            u = f.variables['U'][1:,k,j,i]
                            v = f.variables['V'][1:,k,j,i]
                            wspd = np.sqrt(u**2.+v**2.)
                            w = f.variables['W'][1:,k,j,i]
                            p = f.variables['P'][1:,k,j,i]
                            t = f.variables['T'][1:,k,j,i]
                        if num_lines < 144:
                            print "partial files for WRF: "+ filename
                            u = np.ones((144,))*99.99
                            v = np.ones((144,))*99.99
                            wspd = np.ones((144,))*99.99
                            w = np.ones((144,))*99.99
                            p = np.ones((144,))*99.99
                            t = np.ones((144,))*99.99
                        else:
                            u = np.ones((144,))*99.99
                            v = np.ones((144,))*99.99
                            wspd = np.ones((144,))*99.99
                            w = np.ones((144,))*99.99
                            p = np.ones((144,))*99.99
                            t = np.ones((144,))*99.99
                            counter=counter+1
                        predictors_wrf.append(u)
                        predictors_wrf.append(v)
                        predictors_wrf.append(wspd)
                        predictors_wrf.append(w)
                        predictors_wrf.append(p)
                        predictors_wrf.append(t)
                        u_names = 'u_'+str(k)+'_'+str(j)+'_'+str(i)
                        v_names = 'v_'+str(k)+'_'+str(j)+'_'+str(i)
                        wspd_names = 'wspd_'+str(k)+'_'+str(j)+'_'+str(i)
                        w_names = 'w_'+str(k)+'_'+str(j)+'_'+str(i)
                        p_names = 'p_'+str(k)+'_'+str(j)+'_'+str(i)
                        t_names = 't_'+str(k)+'_'+str(j)+'_'+str(i)
                        names_wrf.append(u_names)
                        names_wrf.append(v_names)
                        names_wrf.append(wspd_names)
                        names_wrf.append(w_names)
                        names_wrf.append(p_names)
                        names_wrf.append(t_names)
    
    
        return zip(predictors_wrf, names_wrf)
    
  3. 使用多处理来完成您的工作。通常,所有计算机都有超过1个CPU核心。当进行大量CPU计算时,多处理将有助于提高速度。根据我以前的经验,多处理将减少大量数据集消耗的时间。

    更新:在2017年2月25日再次测试我的代码文件后,我发现使用8个核心来存储庞大的数据集可以节省90%的折叠时间。

    if __name__ == '__main__':
          from multiprocessing import Pool  # This should be in the beginning statements.
          start_date = '01-01-2017'
          end_date = '01-15-2017'
          fileNames = fileNames(start_date, end_date)
          p = Pool(4) # the cores numbers you want to use.
          results = p.map(dataExtraction, fileNames)
          p.close()
          p.join()
    
  4. 最后,请注意这里的数据结构,因为它非常复杂。希望这可以帮助。如果您有任何其他问题,请留下评论。

答案 2 :(得分:1)

我没有很多建议,但有几点需要注意。

多次打开该文件

首先,你定义这个filename变量,然后在这个循环内部(深入内部:深三个for循环),你正在检查文件是否存在,并且可能在那里打开它(我不知道nc.Dataset做了什么,但我猜它必须打开文件并阅读它:

filename = wrf_path+'\wrfoutRED_d01_'+yy+'-'+mm+'-'+dd+'_'+hour_str+'_00_00'
    for i in i_space:
        for j in j_space:
            for k in k_space:
                    if os.path.isfile(filename):
                        f = nc.Dataset(filename,'r')

这将是非常低效的。如果文件在所有循环之前没有改变,你肯定可以打开一次。

尝试使用更少的for循环

所有这些嵌套的for循环都会使你需要执行的操作数量复杂化。一般建议:尝试使用numpy操作。

使用CProfile

如果你想知道为什么你的程序需要很长时间,最好的方法之一就是分析它们。