用Python编写的程序速度有问题。该程序是“模拟磁场中的铁磁颗粒”,更具体地说是磁惰性液体。该程序可以工作,但与用C ++编写的相同程序相比非常缓慢,但是我用Python编写了一个项目来研究。
总的来说,程序代码基于循环,有很多浮点计算。随机数量的粒子(随机产生的位置)在磁场的影响下相互作用。
这是初始职位:
http://i.stack.imgur.com/T15Bb.jpg
决赛:
http://i.stack.imgur.com/0nU5D.jpg
主循环(在SymMain.py中,具有k变量)迭代是时间步长,计算当时粒子在其中的坐标和作用在其上的力(吸引力和小排斥力)。为了加快速度,我想使用并行处理来同时计算迭代次数,但这是不可能的,因为一次迭代中的计算取决于前一次迭代的计算。
我不知道Python比C ++慢得多。例如,计算一次性步骤中529个粒子的模拟(在我的计算机上):
C + + ~0.5s
Python~50s
那么,如何加快程序?请帮忙。
此代码仅计算,而不是像图片中那样绘制粒子。模拟partcile的数量在SymConst.py上,这是nrH * nrL。
SymMain.py
#coding:windows-1250
from os import system
from SymCalc import *
from SymParticle import *
if __name__ == '__main__':
App = SymCalc()
App.MainLoop()
SymParticle.py
#coding:windows-1250
from random import randint
from math import *
from SymConst import *
from SymParticle import *
class SymCalc(object):
def __init__(self):
# declaration lists containing the properties of the particles
ParticleList = []
ParticleListTemp = []
t = 0.0
# the initial values of particle
for x in range(0, nParticle):
ParticleList.append(Particle(x+1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 8e-6))
ParticleListTemp.append(Particle(x+1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 8e-6))
# generating random coordinates X, Y
for x in range(0, nParticle):
self.Rand(ParticleList[x], x)
# time steps
for k in range(0, k0):
print "Time step = {0}".format(k)
# calculation of forces
for i in range(0, nParticle):
for j in range(0, i-1):
self.ParticleCalculate(ParticleList[i], ParticleList[j], 1)
for j in range(i+1, nParticle):
self.ParticleCalculate(ParticleList[i], ParticleList[j], 1)
# display data
if(k%Constant == 0):
for i in range(0, nParticle):
self.Print(k, i, ParticleList[i], t)
# changing the position of the particle
for i in range(0, nParticle):
self.ChangeOfPosition (ParticleList[i], dt)
# reset forces
for j in range(0, nParticle):
self.ParticleCalculate(ParticleList[i], ParticleList[j], 0)
self.ParticleCalculate(ParticleList[i], ParticleListTemp[j], 0)
# next time step
t += dt
# random coordinates of the particles
def Rand(self, part, lp):
l = lp%nrL # współrzędna X pola
part.x = (l-nrL/2)*(L0/nrL) + ((randint(0,32767)%100)-50)*(L0/nrL-2*part.r)/99 # X
h = (lp+1-(lp)%nrL)/nrL # Y
part.y = (h-nrH/2)*(H0/nrH) + ((randint(0,32767)%100)-50)*(H0/nrH-2*part.r)/99 # współrzędna Y cząstki
# calculating function of the force acting on the particles
def ParticleCalculate(self, part, part_tmp, p):
# auxiliary variables
# r_4, dx, dy, rr, sum, sx, sy, chi_eff, tmp, frepx, frepy, f0
md = []
if(p == 0):
part.frx = 0
part.fry = 0
part.fx = 0
part.fy = 0
if(p == 1):
# versor coordinates connecting the geometrical center of the particle
dx = part.x - part_tmp.x
dy = part.y - part_tmp.y
# the distance between the geometric means of the particle
rr = sqrt(dx*dx + dy*dy)
if(rr < 0.85*(part.r + part_tmp.r)):
print "ERROR: Invalid distance between the particles! Simulation aborted..."
if(rr >= 10*part.r):
# magnetic dipoles
chi_eff = (3.*(MI_P - 1.))/((MI_P - 1) + 3.)
md.append((4.*MI_0*pi*part.r*part.r*part.r*chi_eff*M_H0)/3.0)
md.append((4.*MI_0*pi*part_tmp.r*part_tmp.r*part_tmp.r*chi_eff*M_H0)/3.0)
tmp = pow(rr,7)
# first member
sum = (5.*(md[0]*part.nx*dx + md[0]*part.ny*dy) * (md[1]*part_tmp.nx*dx + md[1]*part_tmp.ny*dy)) / tmp
sx = sum*dx
sy = sum*dy
tmp = tmp / (rr*rr)
# second member
sum = (md[0]*part.nx*md[1]*part_tmp.nx + md[0]*part.ny*md[1]*part_tmp.ny) / tmp
sx -= sum*dx
sy -= sum*dy
# third member
sx -= (md[0]*(md[1]*part_tmp.nx*dx + md[1]*part_tmp.ny*dy)*part.nx + md[1]*(md[0]*part.nx*dx + md[0]*part.ny*dy)*part_tmp.nx)/tmp
sy -= (md[0]*(md[1]*part_tmp.nx*dx + md[1]*part_tmp.ny*dy)*part.ny + md[1]*(md[0]*part.nx*dx + md[0]*part.ny*dy)*part_tmp.ny)/tmp
# finally
tmp = (-3./(4*pi*MI_0))
sx *= tmp
sy *= tmp
part.fx += sx
part.fy += sy
# short-range repulsive force
tmp = pow(rr,15)
r_4 = pow((part.r+part.r),4)
f0 = 3.*fabs(md[0])*fabs(md[1])/(4.*pi*MI_0*r_4)
frepx = pow((part.r+part.r),15)*dx/(tmp*rr)*f0
frepy = pow((part.r+part.r),15)*dy/(tmp*rr)*f0
part.frx += frepx;
part.fry += frepy;
# change the position of the particle
def ChangeOfPosition(self, part, dt):
part.ddx = 0
part.ddy = 0
part.ddx = 1/(6*pi*part.r*eta)*(part.fx+part.frx)*dt
part.ddy = 1/(6*pi*part.r*eta)*(part.fy+part.fry)*dt
# particles new position value
part.x += part.ddx
part.y += part.ddy
if(part.x < -L0/2):
part.x += L0
elif(part.x > L0/2):
part.x -= L0
elif(part.y < -H0/2):
part.y += H0
elif(part.y > H0/2):
part.y -= H0
# display data
def Print(self, k, i, part, t):
print "-"*50
print "\nParticle {0}".format(i+1)
print "The resultant magnetostatic force fx = {0}".format(part.fx - part.frx)
print "The resultant magnetostatic force fy = {0}\n".format(part.fy - part.fry)
if(i == nParticle-1):
print "\n\t\t---t={0}[s]---".format(t)
print "-"*50
SymParticle.py
#coding:windows-1250
class Particle(object):
# generating a particle properties
def __init__(self, num, x, y, fx, fy, frx, fry, nx, ny, ddx, ddy, r):
self.num = num
self.x = x
self.y = y
self.fx = fx
self.fy = fy
self.frx = frx
self.fry = fry
self.nx = nx
self.ny = ny
self.ddx = ddx
self.ddy = ddy
self.r = r
SymConst.py
#coding:windows-1250
### Constant
M_H0 = 3e4
MI_0 = 12.56e-7
MI_P = 2000
eta = 0.1
k0 = 10001
dt = 1e-6 # time step
H0 = 5.95e-4
L0 = 5.95e-4
nrH = 4
nrL = 4
Constant = 100
nParticle = nrH*nrL # number of particle
答案 0 :(得分:5)
要调查此问题,请首先使用Python cProfile模块来分析您的代码并找出所有时间的使用位置(将print语句注释掉):
$ python -m cProfile -s time SymMain.py
30264977 function calls in 34.912 seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
7370737 27.547 0.000 30.501 0.000 SymCalc.py:62(ParticleCalculate)
1 3.626 3.626 34.909 34.909 SymCalc.py:8(__init__)
11101110 1.715 0.000 1.715 0.000 {math.pow}
160016 0.502 0.000 0.502 0.000 SymCalc.py:128(ChangeOfPosition)
4440476 0.498 0.000 0.498 0.000 {method 'append' of 'list' objects}
4440444 0.454 0.000 0.454 0.000 {math.fabs}
2250226 0.287 0.000 0.287 0.000 {math.sqrt}
500154 0.280 0.000 0.280 0.000 {range}
1 0.001 0.001 0.002 0.002 random.py:40(<module>)
1 0.001 0.001 0.001 0.001 hashlib.py:55(<module>)
1 0.001 0.001 0.003 0.003 SymCalc.py:2(<module>)
1616 0.000 0.000 0.000 0.000 SymCalc.py:151(Print)
1 0.000 0.000 34.912 34.912 SymMain.py:3(<module>)
32 0.000 0.000 0.000 0.000 SymParticle.py:4(__init__)
---8<--- snip ---8<---
在您的情况下,大部分时间都花在SymCalc.py:62(ParticleCalculate)
函数上。看看那个函数,它似乎主要是内存访问和计算。这是查看PyPy的好例子!
PyPy是Python的快速,兼容的替代实现 语言(2.7.3和3.2.3)。它有几个优点和独特之处 特性:
- 速度:由于它的Just-in-Time编译器,Python程序通常在PyPy上运行得更快。 (什么是JIT编译器?)
- 内存使用:庞大,需要大量内存的Python程序最终可能比CPython中占用更少的空间。
- 兼容性:PyPy与现有的python代码高度兼容。它支持cffi,可以运行流行的python库 twisted and django。
- 沙盒:PyPy提供了以完全安全的方式运行不受信任的代码的能力。
- Stackless:PyPy默认支持无堆叠模式,为大规模并发提供微线程。
- 以及其他功能。
$ time pypy SymMain.py
real 0m1.071s
user 0m1.025s
sys 0m0.042s
1.071s - 好多了!将其与我系统上的原始执行时间进行比较:
$ time python SymMain.py
real 0m29.766s
user 0m29.646s
sys 0m0.103s