【发布时间】:2021-12-08 10:51:37
【问题描述】:
我用 Python 编写了一个代码,用于逐行读取文件并执行一些平均和求和操作。
我需要加快速度的建议。
pressurefile 中的行数目前为 945,670(它会更高)。
原始代码 这是我发布的原始版本。根据您的建议,我正在优化代码,最后发布了最新版本。
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
我尝试使用 pypy3 并遇到了兼容性问题。我有python3.8和3.9,但是pypy3一直在找3.6然后我放弃了。
选项 4:外部 C 库
我阅读了有关将帮助函数编译为 c 代码并调用 python 的教程。这将是我的下一次尝试。
在 google 中搜索我发现了很多选项,例如 shedskin 等。您能否指出优化上述代码 sn-p 的最佳方法以及可能的替代解决方案以加快速度?
更新 1:2021 年 10 月 21 日至 2021 年 代码是根据下面专家的 cmets 更新的。测试并运行良好。然而,平均代码执行时间从 ~10 秒减少到 ~9.4 秒
压力文件的内容是 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)
测试计算机规格:
Intel® Xeon(R) W-2255 CPU @ 3.70GHz × 20
内存:16 GB
NVIDIA Corporation GP107GL [Quadro P620]
64位 Ubuntu 20.04.3 LTS
当前平均代码执行时间约为 2.6 秒(比原来快 3 倍) 感谢用户@JeromeRichard
【问题讨论】:
-
line.strip("\n")什么都不做。您需要将结果分配回line。 -
很难说没有看到你的文件,但一般来说,如果数据是某种类似于 csv 的东西,像
numpy.genfromtxt这样的东西会胜过任何普通的 python 文件读取。随着计算手段等的额外好处,使用后续的 numpy 数组也会更快。 -
@Barmar:他们可以删除它;无论如何,下一行调用
.split(),之后再也不会使用line;由于 no-argsplit无论如何都会隐式跳过前导/尾随空格,因此即使将strip分配回line也是毫无意义的。 -
math.floor(z/dz)可以只是z // dz -
如果进程受 I/O 限制——听起来可能是这样——那么加快速度的唯一方法就是减少它。将整个文件读入内存,这可以很快完成,然后逐行处理它可能会减轻这种情况。
标签: python numpy performance