文件大小是否会影响python中写入的性能

时间:2014-08-31 18:09:57

标签: python performance file file-io

我试图使用python将 50亿行写入文件。我注意到随着文件越来越大,写入的性能越来越差。

例如,一开始我每秒写入1000万行,经过30亿行后,写入比以前慢10倍。

我想知道这是否与文件的大小有关?

也就是说,如果我将这个大文件分解为较小的文件或者文件的大小不会影响写入的性能,你认为性能会好转吗?

如果您认为它会影响性能,请解释原因吗?

- 更多信息 -

内存消耗始终相同(1.3%)。线的长度是相同的。所以逻辑是我从文件中读取一行(让我们称之为文件A)。文件A的每一行包含2个制表符分隔值,如果其中一个值具有某些特定的特征,我将相同的行添加到文件B.此操作是O(1),我只是将值转换为int并检查该值是否为%someNumber是我想要的7个标志中的任何一个。

每次从文件A读取10M行时,我输出行号。 (多数民众赞成我怎么知道性能下降)。文件B越来越大,对它的写入变慢。

操作系统是Ubuntu。

3 个答案:

答案 0 :(得分:3)

使用这个Python脚本:

from __future__ import print_function
import time
import sys
import platform

if sys.version_info[0]==2:
    range=xrange

times=[]
results=[]
t1=time.time()
t0=t1
tgt=5000000000
bucket=tgt/10
width=len('{:,}  '.format(tgt))
with open('/tmp/disk_test.txt', 'w') as fout:
    for line in range(1,tgt+1):
        fout.write('Line {:{w},}\n'.format(line, w=width))
        if line%bucket==0:
            s='{:15,}   {:10.4f} secs'.format(line, time.time()-t1)
            results.append(s)
            print(s)
            t1=time.time()
    else:
        info=[platform.system(), platform.release(),sys.version, tgt, time.time()-t0]
        s='\n\nDone!\n{} {}\n{} \n\n{:,} lines written in {:10.3f} secs'.format(*info)
        fout.write('{}\n{}'.format(s, '\n'.join(results)))    

print(s)   

在Python 2和OS X下,打印:

    500,000,000     475.9865 secs
  1,000,000,000     484.6921 secs
  1,500,000,000     463.2881 secs
  2,000,000,000     460.7206 secs
  2,500,000,000     456.8965 secs
  3,000,000,000     455.3824 secs
  3,500,000,000     453.9447 secs
  4,000,000,000     454.0475 secs
  4,500,000,000     454.1346 secs
  5,000,000,000     454.9854 secs

Done!
Darwin 13.3.0
2.7.8 (default, Jul  2 2014, 10:14:46) 
[GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] 

5,000,000,000 lines written in   4614.091 secs

在Python 3.4和OS X下:

    500,000,000     632.9973 secs
  1,000,000,000     633.0552 secs
  1,500,000,000     682.8792 secs
  2,000,000,000     743.6858 secs
  2,500,000,000     654.4257 secs
  3,000,000,000     653.4609 secs
  3,500,000,000     654.4969 secs
  4,000,000,000     652.9719 secs
  4,500,000,000     657.9033 secs
  5,000,000,000     667.0891 secs

Done!
Darwin 13.3.0
3.4.1 (default, May 19 2014, 13:10:29) 
[GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] 

5,000,000,000 lines written in   6632.965 secs

生成的文件为139 GB。您可以看到在相对空的磁盘上(我的/tmp路径是3 TB的容量),时间是线性的。

我的怀疑是,在Ubuntu下,你正在运行操作系统试图在EXT4磁盘上保持这个不断增长的文件连续。

回想一下,OS X的HFS +和Linux的EXT4文件系统都使用allocate-on-flush磁盘分配方案。 Linux操作系统还将尝试主动移动文件以允许分配连续(不分段)

对于Linux EXT4 - 您可以预先分配较大的文件以减少此影响。使用fallocate SO帖子中显示的this。然后在Python中回滚文件指针并在适当的位置覆盖。

您可以使用Python truncate方法创建文件,但结果取决于平台。

类似于(伪代码)的东西:

 def preallocate_file(path, size):
     ''' Preallocate of file at "path" of "size" '''
     # use truncate or fallocate on Linux
     # Depending on your platform, You *may* be able to just the following
     # works on BSD and OS X -- probably most *nix:
     with open(path, 'w') as f:
        f.truncate(size)


 preallocate_file(fn, size)
 with open(fn, 'r+') as f:
     f.seek(0)        # start at the beginning 
     # write whatever
     f.truncate()     # erases the unused portion...

答案 1 :(得分:2)

导致此问题的代码不是Python的一部分。如果要写入存在大文件问题的文件系统类型,则需要检查的代码是文件系统驱动程序。

对于变通方法,请为您的平台试验不同的文件系统(但这不再是编程问题,因此不属于StackOverflow)。

答案 2 :(得分:0)

正如你所说的那样,经过30亿行之后,你面临着性能崩溃,你的记忆总是一样(1.3%)!正如其他人提到的,Python I/O代码中没有任何内容会影响基于filesize的性能。因此可能会因软件问题(OS)或硬件问题而发生!为了解决这个问题,我建议采用以下方式:

  • 使用$ time python yourprogram.py命令分析您的时间,向您显示以下结果:

    real - refers to the actual elasped time user - refers to the amount of cpu time spent outside of kernel sys - refers to the amount of cpu time spent inside kernel specific functions

    THIS stachoverflow回答中详细了解realusersys ConcernedOfTunbridgeWells。

  • 使用分析器的逐行时序和执行频率,因此line_profiler是一种简单且不显眼的方式来分析您的代码并用于查看代码的速度和频率每行代码都在脚本中运行。 您可以安装由Robert Kern编写的line_profiler,您可以通过pip安装python包:

    $ pip install line_profiler
    

    阅读文档HERE。你也可以安装memory_profiler查找你的线路使用了多少内存!使用此命令安装:

    $ pip install -U memory_profiler
    $ pip install psutil
    

    和文档HERE

  • 最后一个更重要的方法是找到内存泄漏的地方? cPython解释器使用引用计数作为跟踪内存的主要方法。这意味着每个对象都包含一个计数器,当对象的引用存储在某处时会递增,并在删除对它的引用时递减。当计数器达到零时,cPython解释器知道该对象不再使用,因此删除该对象并释放占用的内存。

    如果对象的引用被保留,即使对象不再使用,程序中也经常会发生内存泄漏。

    找到这些“内存泄漏”的最快方法是使用Marius Gedminas编写的一个名为objgraph的强大工具。此工具允许您查看内存中的对象数量,还可以找到代码中包含对这些对象的引用的所有不同位置。

    使用pipe for install objgraph:

    pip install objgraph
    

    安装此工具后,在代码中插入一个语句以调用调试器:

    import pdb; pdb.set_trace()
    

    哪些对象最常见?

    在运行时,您可以通过运行以下方式检查程序中前20个最常见的对象: 像这样的结果:

    (pdb) import objgraph
    (pdb) objgraph.show_most_common_types()
    
    MyBigFatObject             20000
    tuple                      16938
    function                   4310
    dict                       2790
    wrapper_descriptor         1181
    builtin_function_or_method 934
    weakref                    764
    list                       634
    method_descriptor          507
    getset_descriptor          451
    type                       439
    

    所以请阅读文档HERE

    来源: http://mg.pov.lt/objgraph/#python-object-graphs

    https://pypi.python.org/pypi/objgraph

    http://www.appneta.com/blog/line-profiler-python/

    https://sublime.wbond.net/packages/LineProfiler

    http://www.huyng.com/posts/python-performance-analysis/

    What do 'real', 'user' and 'sys' mean in the output of time(1)?