我已经用Python编写了一段代码来逐行读取文件,并执行一些平均和求和操作
我需要加快速度的建议
目前pressurefile
中的行数为945670(将更高)
原始代码 这是我发布的原始版本。根据你的建议,我正在优化代码,最后发布了最新版本
def time_average():
try:
filename = mem.pressurefile
navg = mem.NFRAMES
dz = mem.dz
zlo = mem.zlo
NZ = mem.NZ
mass = mem.mass
dens_fact = amu_to_kg / (mem.slab_V * ang3_to_m3)
array_pxx = np.zeros([NZ,1])
array_pyy = np.zeros([NZ,1])
array_pzz = np.zeros([NZ,1])
array_ndens = np.zeros([NZ,1])
array_density = np.zeros([NZ,1])
array_enthalpy = np.zeros([NZ,1])
array_surf_tens = np.zeros([NZ,1])
counter = 0
with open(filename) as f:
for line in f:
line.strip("\n")
#content = [_ for _ in line.split()]
content = line.split()
if len(content) == 7:
z = float(content[3]) - zlo
pxx = float(content[4])
pyy = float(content[5])
pzz = float(content[6])
loc = math.floor(z/dz)
if loc >= NZ:
loc = loc - NZ
elif loc < 0:
loc = loc + NZ
#print(z, loc, zlo)
array_pxx[loc] += pxx
array_pyy[loc] += pyy
array_pzz[loc] += pzz
array_ndens[loc] += 1
counter += 1
for col in range(NZ):
array_pxx[col] /= navg
array_pyy[col] /= navg
array_pzz[col] /= navg
array_ndens[col] /= navg
array_density[col] = mass * dens_fact * array_ndens[col]
return (array_density, array_enthalpy, array_surf_tens)
except IndexError as err:
writelog (err)
writelog(float(content[3]) , loc, zlo)
到目前为止,我已经尝试了以下选项:
剖析:
使用cprofile配置主代码,并确定上述辅助函数对于74.4MB文件需要10秒。对我来说,这10秒是很高的
选项1:cython3
使用cython编译,如下所示
cython3 --embed -o ptythinfile.c ptythinfile.py
gcc -Os -I /usr/include/python3.8 -o ptythinfile ptythinfile.c -lpython3.8 -lpthread -lm -lutil -ldl
这并没有带来任何性能改进
选项2:C/C++
将整个代码转换为C/C++并编译
事实上,我的第一个代码是C++,调试是个噩梦,切换到Python。所以,我不想走这条路选项3:Pypy3
我尝试使用Py3,但遇到了兼容性问题。我有python3.8和3.9,但pypy3一直在寻找3.6,然后我放弃了
选项4:外部C库
我阅读了有关将helper函数编译为c代码并调用python的教程。这将是我的下一次尝试
在谷歌搜索中,我发现了许多选项,如shedskin等。您能否指出优化上述代码片段的最佳方法以及可能的替代解决方案,以加快速度
更新1:2021年10月21日 代码根据以下专家的评论进行更新。测试和工作良好。但是,平均代码执行时间从~10秒减少到~9.4秒
pressurefile的内容是LAMMPS软件的输出,其前几行如下所示:
ITEM: TIMESTEP
50100
ITEM: NUMBER OF ATOMS
2744
ITEM: BOX BOUNDS pp pp pp
-2.5000000000000000e+01 2.5000000000000000e+01
-2.5000000000000000e+01 2.5000000000000000e+01
-7.5000000000000000e+01 7.5000000000000000e+01
ITEM: ATOMS id x y z c_1[1] c_1[2] c_1[3]
2354 18.8358 -21.02 -70.5731 -21041.8 -3738.18 -2520.84
1708 5.54312 -8.1526 -62.6984 4362.84 -30610.2 -4065.84
最后两行是我们需要处理的
最新代码
def time_average():
try:
filename = mem.pressurefile
navg = mem.NFRAMES
dz = mem.dz
zlo = mem.zlo
NZ = mem.NZ
mass = mem.mass
dens_fact = amu_to_kg / (mem.slab_V * ang3_to_m3)
array_pxx = np.zeros([NZ,1])
array_pyy = np.zeros([NZ,1])
array_pzz = np.zeros([NZ,1])
array_ndens = np.zeros([NZ,1])
#array_density = np.zeros([NZ,1])
array_enthalpy = np.zeros([NZ,1])
array_surf_tens = np.zeros([NZ,1])
counter = 0
locList = []
pxxList = []
pyyList = []
pzzList = []
with open(filename) as f:
for line in f:
#line.strip("\n")
#content = [_ for _ in line.split()]
content = line.split()
if len(content) == 7:
z = float(content[3]) - zlo
pxx = float(content[4])
pyy = float(content[5])
pzz = float(content[6])
#loc = math.floor(z/dz)
loc = int(z // dz)
if loc >= NZ:
loc = loc - NZ
elif loc < 0:
loc = loc + NZ
#print(z, loc, zlo)
# Not great but much faster than using Numpy functions
locList.append(loc)
pxxList.append(pxx)
pyyList.append(pyy)
pzzList.append(pzz)
counter += 1
# Very fast list-to-Numpy-array conversion
locList = np.array(locList, dtype=np.int32)
pxxList = np.array(pxxList, dtype=np.float64)
pyyList = np.array(pyyList, dtype=np.float64)
pzzList = np.array(pzzList, dtype=np.float64)
# Fast accumulate
np.add.at(array_pxx[:,0], locList, pxxList)
np.add.at(array_pyy[:,0], locList, pyyList)
np.add.at(array_pzz[:,0], locList, pzzList)
np.add.at(array_ndens[:,0], locList, 1)
array_pxx /= navg
array_pyy /= navg
array_pzz /= navg
array_ndens /= navg
array_density = mass * dens_fact * array_ndens
return (array_density, array_enthalpy, array_surf_tens)
except IndexError as err:
writelog (err)
print(loc)
writelog(float(content[3]) , loc, zlo)
测试计算机规格:
英特尔至强(R)W-2255CPU@3.70GHz×20
内存:16 GB
英伟达公司GP107GL[Quadro P620]
64位Ubuntu 20.04.3 LTS
当前平均代码执行时间约为2.6s(3x比原始代码快) 用户@JeromeRichard的信用
读取文件的第一步可以通过
genfromtxt
轻松完成。这会逐行读取文件,将其拆分(就像您所做的那样),将结果收集到列表列表中,并在最后生成数组pandas.read_csv
更快,至少在使用c
模式时是这样,对于大文件,可能值得一试生成保留第一列整数性质的结构化数组。通过字段名访问“columns”(如数据类型中所指定):
或以浮点形式加载所有值,生成(N,7)2d数组:
将
usecols
指定为[3,4,5,6]
可能会节省一些时间。您似乎只是对以下数据感兴趣:然后,您似乎对
z
执行了一些操作来派生一个loc
,并使用它来组合“pxyz”数组的“行”。我不想再重复了无论如何,通常在处理大型
csv
文件时,我们一步读取,然后稍后处理生成的数组或数据帧。阅读时进行处理是可能的,但通常不值得付出努力首先,Python显然不是高效进行此类计算的最佳工具。代码是顺序的,大部分时间花在CPython解释器操作或Numpy内部函数上
这部分是因为未启用优化。您需要使用标志
-O2
甚至-O3
。不过,Cython可能帮不了什么忙,因为大部分时间都花在CPython上,在这个特定代码中进行Numpy调用您不需要移植所有代码。您只能重写像这样的性能关键型函数,并将它们放在专用的CPython模块中(即编写C/C++扩展)。但是,此解决方案需要处理低级别的CPython内部构件。Cython有助于处理:AfAIK,可以使用Cython调用Cython函数中的C++函数,Cython帮助轻松地执行Cpython和C++函数之间的接口。简单的函数界面应该有助于使代码更易于阅读和维护。不过,我同意这不是很好,但是C++代码可以比cPython更快地计算这个数量。p>
谢德斯金不再积极发展。我怀疑这样一个项目在您的情况下能有所帮助,因为代码非常复杂,并且使用Numpy
Numba理论上对这种情况有很大帮助。但是,字符串还没有得到很好的支持(即解析)
像
array_pxx[loc] += pxx
这样的行非常慢,因为解释器需要在内部调用C Numpy函数,该函数执行许多不必要的操作:绑定/类型检查、类型转换、分配/释放、引用计数等。这样的操作非常慢(比C++慢1000倍以上)。避免这种情况的一个解决方案是在纯Python循环中使用纯Python列表(至少在代码无法有效矢量化时)。您可以高效地将列表转换为Numpy数组,并使用np.add.at
执行累加。下面是一个改进的实现:在我的机器上,此代码的速度大约快3倍。但是请注意,它应该占用更多内存(由于列表)
剩下的大部分时间都花在字符串转换(25%)、字符串拆分(20-25%)、列表追加(17%)和CPython解释器本身上,就像导入模块一样(20%)。I/O操作只占总时间的一小部分(在SSD上或文件被操作系统缓存时)。只要使用纯Python代码(与CPython一起使用),优化这一点就具有挑战性
相关问题 更多 >
编程相关推荐