首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >怎样才能最快地逐行读取文件?

怎样才能最快地逐行读取文件?
EN

Stack Overflow用户
提问于 2021-10-21 16:53:31
回答 2查看 180关注 0票数 0

我用Python编写了一段代码,可以逐行读取文件,并执行一些平均值和求和操作。

我需要关于加快速度的建议。

目前,pressurefile中的行数为945,670行(将更高)。

原始代码这是我发布的原始版本。根据您的建议,我正在优化代码,并在最后发布了最新版本。

代码语言:javascript
复制
    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 s。对我来说,这10分钟太高了.

选项1:cython3

使用cython编译,如下所示。

代码语言:javascript
复制
    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库

我阅读了关于将helper函数编译为c代码并调用到python的教程。这是我的下一次尝试。

在谷歌搜索时,我发现了很多选项,比如谢德皮等等。你能指出优化上述代码片段的最佳方法,以及加快速度的可能替代解决方案吗?

更新1:10月21-2021代码是根据以下专家的评论进行更新的。经过测试并运行良好。但是,平均代码执行时间从~10s缩短到~9.4s。

压力文件的内容是LAMMPS软件的输出,其前几行如下所示:

代码语言:javascript
复制
    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

最后两行是我们需要处理的。

最新代码

代码语言:javascript
复制
    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公司GP107GL Quadro P620

64位Ubuntu 20.04.3 LTS

当前平均代码执行时间为~2.6s (3x比原始代码快),归功于用户@JeromeRichard

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-10-22 17:50:46

首先,Python显然不是高效执行此类计算的最佳工具。代码是连续的,大部分时间用于CPython解释器操作或Numpy内部函数。

选项1: cython3

这并没有带来任何性能改进。

这部分是因为优化没有启用。您需要使用标志-O2,甚至-O3。尽管如此,Cython可能不会有很大帮助,因为大部分时间都花在这个特定代码中的CPython到Numpy调用上。

选项2: C/C++将整个代码转换为C/C++并编译它。事实上,我的第一段代码是用C++编写的,而调试则是一场噩梦,转而使用python。所以我不想走这条路。

您不需要移植所有代码。您只能重写像这样的性能关键函数,并将它们放入一个专用的CPython模块(即。编写C/C++扩展)。然而,这个解决方案需要处理低级别的CPython内部。Cython可能有助于解决这个问题: AFAIK,您可以使用Cython从Cython函数调用C++函数,Cython帮助轻松地执行CPython和C++函数之间的接口。简单的函数接口应该有助于使代码更易于阅读和维护。不过,我同意这并不好,但C++代码至少可以比CPython快一个数量级.

搜索谷歌,我发现了许多选择,如舍德皮等。

ShedSkin不再是积极开发的。我怀疑这样的项目对您的情况有帮助,因为代码非常复杂,并且使用Numpy。

在这种情况下,Numba理论上可以帮上很多忙。然而,字符串还没有得到很好的支持。(解析)

,您能指出优化上述代码片段的最佳方法以及加快速度的可能的替代解决方案吗?

array_pxx[loc] += pxx这样的行非常慢,因为解释器需要在内部调用C++函数,该函数执行许多不必要的操作:绑定/类型检查、类型转换、分配/释放位置、引用计数等等。这样的操作非常慢(超过C++的1000倍)。避免这种情况的一个解决方案是,在纯Python循环中使用纯Python列表(至少当代码无法有效地向量化时)。您可以高效地将列表转换为Numpy数组,并使用np.add.at.执行累积操作。以下是一个改进的实现:

代码语言:javascript
复制
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)

在我的机器上,这段代码的速度大约是的3倍。但是,请注意,它应该占用更多的内存(由于列表)。

剩下的大部分时间用于字符串转换(25%)、字符串拆分(20-25%)、列表附加(17%)和CPython解释器本身,比如导入模块(20%)。I/O操作只占整个时间的一小部分(在SSD上或当文件被操作系统缓存时)。只要使用纯Python代码(使用CPython),优化这一点就具有挑战性。

票数 2
EN

Stack Overflow用户

发布于 2021-10-22 18:58:44

读取文件的第一步可以很容易地用genfromtxt完成。它会逐行读取文件,将其拆分(就像您所做的那样),收集列表中的结果,并在最后生成数组。pandas.read_csv更快,至少在使用c模式时是这样,对于大型文件来说可能值得一试。

创建一个保留第一列的整数性质的结构化数组。对“列”的访问按字段名(如dtype中所指定):

代码语言:javascript
复制
In [30]: data = np.genfromtxt('stack69665939.py',skip_header=9, dtype=None)
In [31]: data
Out[31]: 
array([(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)],
      dtype=[('f0', '<i8'), ('f1', '<f8'), ('f2', '<f8'), ('f3', '<f8'), ('f4', '<f8'), ('f5', '<f8'), ('f6', '<f8')])

或将所有值作为浮点数加载,生成一个(N,7) 2d数组:

代码语言:javascript
复制
In [32]: data = np.genfromtxt('stack69665939.py',skip_header=9)
In [33]: data
Out[33]: 
array([[ 2.35400e+03,  1.88358e+01, -2.10200e+01, -7.05731e+01,
        -2.10418e+04, -3.73818e+03, -2.52084e+03],
       [ 1.70800e+03,  5.54312e+00, -8.15260e+00, -6.26984e+01,
         4.36284e+03, -3.06102e+04, -4.06584e+03]])

usecols指定为[3,4,5,6]可能会节省一些时间。你似乎只是对这些数据感兴趣:

代码语言:javascript
复制
In [35]: z = data[:,3]
In [36]: pxyz = data[:,[4,5,6]]
In [37]: z
Out[37]: array([-70.5731, -62.6984])
In [38]: pxyz
Out[38]: 
array([[-21041.8 ,  -3738.18,  -2520.84],
       [  4362.84, -30610.2 ,  -4065.84]])

然后,您似乎使用z来派生loc,并使用它组合“`pxyz”数组的“行”。我不会试图重新创造这个的。

无论如何,通常在处理大型csv文件时,我们先一步读取,然后再处理结果数组或数据。阅读时进行处理是可能的,但通常不值得付出努力。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69665939

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档