Python 并行文件写入是否有效?

Python 并行文件写入是否有效?,python,io,parallel-processing,Python,Io,Parallel Processing,我想知道并行文件写入是否有效。实际上,硬盘一次只有一个可用的读磁头。因此,硬盘驱动器一次只能执行一项任务。 但下面的测试(python)与我的预期相矛盾: 要复制的文件大约为1 Gb 脚本1(//对同一文件逐行读写10次的任务): 脚本2(对同一文件逐行读写10次的任务): 脚本3(//将同一文件复制10次的任务): 脚本4(将同一文件复制10次的任务): 结果: $ # // task to read and write line by line 10 times a same file $

我想知道并行文件写入是否有效。实际上,硬盘一次只有一个可用的读磁头。因此,硬盘驱动器一次只能执行一项任务。 但下面的测试(python)与我的预期相矛盾:

要复制的文件大约为1 Gb

脚本1(//对同一文件逐行读写10次的任务):

脚本2(对同一文件逐行读写10次的任务):

脚本3(//将同一文件复制10次的任务):

脚本4(将同一文件复制10次的任务):

结果:

$ # // task to read and write line by line 10 times a same file
$ time python read_write_1.py

real    1m46.484s
user    3m40.865s
sys 0m29.455s

$ rm test_jm*
$ # task to read and write line by line 10 times a same file
$ time python read_write_2.py

real    4m16.530s
user    3m41.303s
sys 0m24.032s

$ rm test_jm*
$ # // task to copy 10 times a same file
$ time python read_write_3.py

real    1m35.890s
user    0m10.615s
sys 0m36.361s


$ rm test_jm*
$ # task to copy 10 times a same file
$ time python read_write_4.py

real    1m40.660s
user    0m7.322s
sys 0m25.020s
$ rm test_jm*
这些基本结果似乎表明//io读写效率更高

谢谢你的光

我想知道并行文件写入是否有效

简短回答:同时从多个线程物理写入同一磁盘的速度永远不会比从一个线程写入该磁盘的速度快(这里讨论的是普通硬盘)。在某些情况下,它甚至可以慢得多

但是,一如既往,这取决于许多因素:

  • 操作系统磁盘缓存:写操作通常由操作系统保存在缓存中,然后分块写入磁盘。因此,多个线程可以毫无问题地同时写入该缓存,并且这样做具有速度优势。尤其是当数据的处理/准备时间超过磁盘的写入速度时

  • 在某些情况下,即使从多个线程直接写入物理磁盘,操作系统也会对此进行优化,并且只向每个文件写入大数据块

  • 然而,在最坏的情况下,每次都可能将较小的块写入磁盘,导致每个文件交换机都需要进行硬盘寻道(在普通hdd上为±10ms!)(在SSD上进行相同操作不会太糟糕,因为有更多的直接访问,不需要寻道)

因此,一般来说,当同时从多个线程写入磁盘时,最好在内存中准备(一些)数据,并使用某种锁将最终数据以较大的块写入磁盘,或者从一个专用写入线程写入。如果文件在写入时正在增长(即前面没有设置文件大小),将数据写入较大的块也可以防止磁盘碎片(至少尽可能多)

在某些系统上可能根本没有区别,但在另一些系统上可能会有很大的区别,速度会慢很多(甚至在具有不同硬盘的同一系统上)

为了更好地测试单线程与多线程的写入速度差异,总文件大小必须大于可用内存,或者至少在测量结束时间之前,所有缓冲区都应该刷新到磁盘。仅测量将数据写入操作系统磁盘缓存所需的时间在这里没有多大意义

理想情况下,将所有数据写入磁盘的总时间应等于物理硬盘的写入速度。如果使用一个线程写入磁盘的速度比磁盘写入速度慢(这意味着处理数据的时间比写入数据的时间长),那么使用更多线程显然会加快速度。如果从多个线程写入的速度低于磁盘写入速度,则由于在不同文件(或同一个大文件中的不同块)之间切换而导致的磁盘寻道将丢失时间

为了了解执行大量磁盘查找时的时间损失,让我们看一些数字:

比如说,我们有一个写速度为50MB/s的硬盘:

  • 写入一个50MB的连续块需要1秒(在理想情况下)

  • 在1MB的块中执行同样的操作,并在文件切换和结果之间进行磁盘寻道将给出:20ms写入1MB+10ms寻道时间。写入50MB需要1.5秒。这在时间上增加了50%,只是在两者之间做了一个快速搜索(从磁盘读取也一样——考虑到更快的读取速度,差异甚至会更大)

实际上,它将介于两者之间,具体取决于系统


虽然我们希望操作系统能够很好地处理所有这些问题(或者通过使用示例),情况并非总是如此。

你有什么证据证明硬盘只有一个读头?你写入文件的任何数据都会存储在你的计算机RAM中,直到你的计算机操作系统开始整理并很快将其写入磁盘。你期望得到什么样的结果?你所接受的答案是这样的//io不应该更高效。似乎我的结果显示//io比efficient@ScottHunter我的意思是,一个硬盘驱动器在给定的时间只能有一个磁头读写硬盘。正如在这里解释->谢谢@danny\u ds为您提供了真正完整的答案。这就是我的想法:目前,我不知道如何解释复制10倍的1GB文件的//io更快。也许正如你解释的那样,我的硬盘有一个大的缓存。@bioinformnatics-我将仔细查看你的代码和数据,但一个好的测量方法是,测量的总写入速度应该等于硬盘的物理写入速度(即复制相同大小的文件所需的时间)。如果您在使用多线程时感觉更好,这几乎肯定意味着处理时间要比写入时间长。另外,在测量(总)物理写入速度时,请确保所有数据都已刷新到磁盘。@bioinformnatics-请参阅我的更新答案。我看到,在测试中,您在循环中使用相同的文件,因此所有内容都可以在缓存中。该测试还使用了readline和writeline,这不是将数据写入磁盘的最快方式(可能比磁盘写入速度慢,不确定)。另一个(复制)测试可能是:复制磁盘上的文件(比可用内存大10倍)并计时。然后在1和多个线程中执行相同的操作。在这两种情况下,请尝试获得与te手动复制相同的时间。具体取决于
#!/usr/bin/env python
def read_and_write( copy_filename ):
    with open( "/env/cns/bigtmp1/ERR000916_2.fastq", "r") as fori:
        with open( "/env/cns/bigtmp1/{}.fastq".format( copy_filename) , "w" ) as fout:
            for line in fori:
                fout.write( line + "\n" )
    return copy_filename

def main():
    f_names = [ "test_jm_{}".format(i) for i in range( 0, 10 ) ]
    for n in f_names:
        result = read_and_write( n )

if __name__ == "__main__":
    main()
#!/usr/bin/env python
from shutil import copyfile
from multiprocessing import Pool
def read_and_write( copy_filename ):
    copyfile( "/env/cns/bigtmp1/ERR000916_2.fastq", "/env/cns/bigtmp1/{}.fastq".format( copy_filename) )
    return copy_filename

def main():
    f_names = [ "test_jm_{}".format(i) for i in range( 0, 10 ) ]
    pool = Pool(processes=4)
    results = pool.map( read_and_write, f_names )

if __name__ == "__main__":
    main()
#!/usr/bin/env python
from shutil import copyfile
def read_and_write( copy_filename ):
    copyfile( "/env/cns/bigtmp1/ERR000916_2.fastq", "/env/cns/bigtmp1/{}.fastq".format( copy_filename) )
    return copy_filename

def main():
    f_names = [ "test_jm_{}".format(i) for i in range( 0, 10 ) ]
    for n in f_names:
        result = read_and_write( n )

if __name__ == "__main__":
    main()
$ # // task to read and write line by line 10 times a same file
$ time python read_write_1.py

real    1m46.484s
user    3m40.865s
sys 0m29.455s

$ rm test_jm*
$ # task to read and write line by line 10 times a same file
$ time python read_write_2.py

real    4m16.530s
user    3m41.303s
sys 0m24.032s

$ rm test_jm*
$ # // task to copy 10 times a same file
$ time python read_write_3.py

real    1m35.890s
user    0m10.615s
sys 0m36.361s


$ rm test_jm*
$ # task to copy 10 times a same file
$ time python read_write_4.py

real    1m40.660s
user    0m7.322s
sys 0m25.020s
$ rm test_jm*