Как ускорить программу на Python 2 с помощью нескольких вложенных циклов for - PullRequest
0 голосов
/ 07 февраля 2019

Этот код имеет несколько циклов for, и списки, в которых я читаю, имеют 999 баллов в каждом.Я хочу повторить это до 10000 раз.Однако даже повторение всего 2 раза занимает около 10 минут.

Несмотря на то, что я публикую этот конкретный код, я думаю, что ответ на мой вопрос может помочь другим быстрее выполнить свои коды с большим количеством данных.

Любой ваш совет приветствуется.Большое спасибо.

Что этот код делает: По сути, я читаю в массивах из текстового файла в виде списков.Каждый список (например, x1, y1, z1 ... и т. Д.) Содержит 999 элементов каждый.Я оперирую каждым элементом в списке на основе других элементов (двух внутренних циклов).Конечный результат - совершенно новый список, который я назвал х2.Затем предполагается, что этот код повторяет операции «n # of times» (внешний цикл).

Моя проблема в том, что я могу повторить это a только на несколько итераций, прежде чем потребуется много времени для выполнения.

import matplotlib.pyplot as plt
from astropy.table import Table
from astropy.io import ascii
import numpy as np
import argparse
import time
#for 200
start_time = time.time()

npoints=999
n1, mass1, x1, y1,z1,vx1,vy1,vz1,fx_list,fy_list,fz_list= [],[],[],[],[],[],[],[],[],[],[]
AngL_list=[]
Etot0_list=[]
G=1
dt=.01

with open('homo_sph_N1000_R3_v1.dat') as f:
     for row in f.readlines():  
        if not row.startswith("#"):
            spaces=row.split('   ')
            n1.append(float(spaces[0]))
            mass1.append(float(spaces[1]))
            x1.append(float(spaces[2]))
            y1.append(float(spaces[3]))
            z1.append(float(spaces[4]))
            vx1.append(float(spaces[5]))
            vy1.append(float(spaces[6]))
            vz1.append(float(spaces[7]))

for n in range(2):
#changes the particle on which the forces are acting
     for xn in range(0,npoints):
     #changes the forces from other particles acting on the particle
          for step in range(0,npoints):
          #Here we find the accelearation for every particle
               fx=((G*mass1[xn]*mass1[step+1]*((x1[step+1]**2.+y1[step+1]**2.+z1[step+1]**2.)-(x1[xn]**2.+y1[xn]**2.+z1[xn]**2.)))/  (  abs((x1[step+1]**2.+y1[step+1]**2.+z1[step+1]**2.)-(x1[xn]**2.+y1[xn]**2.+z1[xn]**2.))**2.+(.2)**2  )**(3./2.))

               fy=((G*mass1[xn]*mass1[step+1]*((x1[step+1]**2.+y1[step+1]**2.+z1[step+1]**2.)-(x1[xn]**2.+y1[xn]**2.+z1[xn]**2.)))/  (    abs((x1[step+1]**2.+y1[step+1]**2.+z1[step+1]**2.)-(x1[xn]**2.+y1[xn]**2.+z1[xn]**2.))**2+(.2)**2 )**(3./2.))

               fz=((G*mass1[xn]*mass1[step+1]*((x1[step+1]**2.+y1[step+1]**2.+z1[step+1]**2.)-(x1[xn]**2.+y1[xn]**2.+z1[xn]**2.)))/  (    abs((x1[step+1]**2.+y1[step+1]**2.+z1[step+1]**2.)-(x1[xn]**2.+y1[xn]**2.+z1[xn]**2.))**2+(.2)**2 )**(3./2.))

               #Then put store it in an array
               fx_list.append(fx)
               fy_list.append(fy)
               fz_list.append(fz)


          #Now, I need to split that array up by npoints, each particle has npoints forces acting on it. 
          fxx= np.array_split(fx_list,npoints)
          fyy= np.array_split(fy_list,npoints)
          fzz= np.array_split(fz_list,npoints)

          #since the force on a particle is the sum of all forces acting on it, I'm summing each variable in each array together. e.g. [1,2,3]=[6] 
          fxxx_list=[]
          fyyy_list=[]
          fzzz_list=[]
          for xn in range(0,npoints):
               fxxx= np.sum(fxx[xn])
               fyyy= np.sum(fyy[xn])
               fzzz= np.sum(fzz[xn]) 

               #and save that in array. Now I have the accelearation on each particle. 
               fxxx_list.append(fxxx)
               fyyy_list.append(fyyy)
               fzzz_list.append(fzzz) 

          #This is where i begin the integration

          vx2=[]
          vy2=[]
          vz2=[] 
          for xn in range(0,npoints):

               vx11=vx1[xn]+.5*(fxxx_list[xn]+fxxx_list[xn])*dt
               vy11=vy1[xn]+.5*(fyyy_list[xn]+fyyy_list[xn])*dt
               vz11=vz1[xn]+.5*(fzzz_list[xn]+fyyy_list[xn])*dt 

               vx2.append(vx11)
               vy2.append(vy11)
               vz2.append(vz11) 

          x2=[]
          y2=[]
          z2=[]
          for xn in range(0,npoints):
               x11=(x1[xn]+vx2[xn]*dt)+(.5*fxxx_list[xn]*(dt**2))
               y11=(y1[xn]+vy2[xn]*dt)+(.5*fyyy_list[xn]*(dt**2))
               z11=(z1[xn]+vz2[xn]*dt)+(.5*fzzz_list[xn]*(dt**2)) 

               x2.append(x11)
               y2.append(y11)
               z2.append(z11)

x1,y1,z1,vx1,vy1,vz1 = x2,y2,z2,vx2,vy2,vz2

print x2,y2 
plt.scatter(x2,y2)

print("--- %s seconds ---" % (time.time() - start_time))    

plt.show()

Ответы [ 2 ]

0 голосов
/ 14 февраля 2019

Я думаю, что вы сможете получить большое ускорение (возможно, порядка 1000 раз), если вы преобразуете свои входные данные в массивы numpy, выполняете операции с массивами numpy и предварительно выделяете массивы numy до их требуемого размера, а нечем использовать списки и добавлять их по ходу работы.

Например, просто взяв начало вашего примера, вы можете сделать что-то вроде следующего (хотя я не могу гарантировать, что он делает именно то, что вы хотите, но этопросто руководство)

with open('homo_sph_N1000_R3_v1.dat') as f:
    for row in f.readlines():  
        if not row.startswith("#"):
            spaces=row.split('   ')
            n1.append(float(spaces[0]))
            mass1.append(float(spaces[1]))
            x1.append(float(spaces[2]))
            y1.append(float(spaces[3]))
            z1.append(float(spaces[4]))
            vx1.append(float(spaces[5]))
            vy1.append(float(spaces[6]))
            vz1.append(float(spaces[7]))

# convert to numpy arrays
n1 = np.array(n1)
mass1 = np.array(mass1)
# KEEP DOING THIS FOR THE OTHER INPUTS

for n in range(2):
    # PREALLOCATE
    fx = np.zeros(npoints, npoints-1)
    fy = np.zeros(npoints, npoints-1)
    fz = np.zeros(npoints, npoints-1)

    #changes the particle on which the forces are acting
    for xn in range(0,npoints):
        #changes the forces from other particles acting on the particle

        # REMOVE THE INNER FOR LOOP AND JUST USE THE ARRAYS
        #for step in range(0,npoints):
        #Here we find the accelearation for every particle
        fx[xn] = ((G*mass1[xn]*mass1[1:]*((x1[1:]**2.+y1[1:]**2.+z1[1:]**2.)-(x1[xn]**2.+y1[xn]**2.+z1[xn]**2.)))/  (  abs((x1[1:]**2.+y1[1:]**2.+z1[1:]**2.)-(x1[xn]**2.+y1[xn]**2.+z1[xn]**2.))**2.+(.2)**2  )**(3./2.))

        fy[xn] = ((G*mass1[xn]*mass1[1:]*((x1[1:]**2.+y1[1:]**2.+z1[1:]**2.)-(x1[xn]**2.+y1[xn]**2.+z1[xn]**2.)))/  (    abs((x1[1:]**2.+y1[1:]**2.+z1[1:]**2.)-(x1[xn]**2.+y1[xn]**2.+z1[xn]**2.))**2+(.2)**2 )**(3./2.))

        fz[xn] = ((G*mass1[xn]*mass1[1:]*((x1[1:]**2.+y1[1:]**2.+z1[1:]**2.)-(x1[xn]**2.+y1[xn]**2.+z1[xn]**2.)))/  (    abs((x1[1:]**2.+y1[1:]**2.+z1[1:]**2.)-(x1[xn]**2.+y1[xn]**2.+z1[xn]**2.))**2+(.2)**2 )**(3./2.))

      #Now, I need to split that array up by npoints, each particle has npoints forces acting on it. 
      fxx= np.array_split(fx,npoints)
      fyy= np.array_split(fy,npoints)
      fzz= np.array_split(fz,npoints)

      #since the force on a particle is the sum of all forces acting on it, I'm summing each variable in each array together. e.g. [1,2,3]=[6] 
      fxxx= np.sum(fxx[xn], axis=1)
      fyyy= np.sum(fyy[xn], axis=1)
      fzzz= np.sum(fzz[xn], axis=1) 
0 голосов
/ 07 февраля 2019

Это только небольшое ускорение, но код, похоже, много делает x**2 (х в квадрате).

В python3 обычно медленнее выполняется x**2, чем x*x.Рассмотрим простую тестовую программу:

import time

iteration_count=99999999

# Do a lot of squaring with the ** operator
start1 = time.time()
sum = 0
for i in range( iteration_count ):
    sum += i ** 2
end1 = time.time()


# Do a lot of squaring with i * i
start2 = time.time()
sum = 0
for i in range( iteration_count ):
    sum += i * i
end2 = time.time()

print("x**2 => %f seconds" % (end1-start1))
print("x*x  => %f seconds" % (end2-start2))

, которая дает мне результаты:

$ python3 ./squared.py 
x**2 => 21.347830 seconds
x*x  => 8.983334 seconds

Я запускал ее несколько раз, она не сильно отличается.

Код в вопросе делает много вычислений, чтобы сделать fx, fy и fz (Кажется, это одинаково для каждого? Это правильно?) Если в этих вычислениях есть какая-то общность,промежуточные результаты должны быть удалены и рассчитаны только один раз.

Например, вместо:

fx=((G*mass1[xn]*mass1[step+1]*((x1[step+1]**2.+y1[step+1]**2.+z1[step+1]**2.) ...
fy=((G*mass1[xn]*mass1[step+1]*((x1[step+1]**2.+y1[step+1]**2.+z1[step+1]**2.) ...
fz=((G*mass1[xn]*mass1[step+1]*((x1[step+1]**2.+y1[step+1]**2.+z1[step+1]**2.) ...

Первая часть должна быть вычислена только один раз:

g_mass = G*mass1[xn]*mass1[step+1]
fx=((g_mass * ((x1[step+1]**2.+y1[step+1]**2.+z1[step+1]**2.) ...
fy=((g_mass * ((x1[step+1]**2.+y1[step+1]**2.+z1[step+1]**2.) ...
fz=((g_mass * ((x1[step+1]**2.+y1[step+1]**2.+z1[step+1]**2.) ...

Ианалогично для любых частей этих формул с общими компонентами.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...