多线程文件复制比多核CPU上的单个线程慢得多

时间:2011-12-21 03:37:18

标签: python multithreading file copy queue

我正在尝试用Python编写多线程程序来加速(低于1000).csv文件的复制。多线程代码比顺序方法运行得更慢。我用profile.py计算了代码。我相信我一定做错了什么,但我不确定是什么。

环境:

  • 四核CPU。
  • 2个硬盘,一个包含源文件。另一个是目的地。
  • 1000个csv文件,大小从几KB到10 MB不等。

方法:

我将所有文件路径放在队列中,并创建4-8个工作线程从队列中提取文件路径并复制指定的文件。在任何情况下,多线程代码都不会更快:

  • 顺序复制需要150-160秒
  • 线程复制需要230秒

我认为这是一个I / O绑定任务,因此多线程应该有助于提高操作速度。

守则:

    import Queue
    import threading
    import cStringIO 
    import os
    import shutil
    import timeit  # time the code exec with gc disable
    import glob    # file wildcards list, glob.glob('*.py')
    import profile # 

    fileQueue = Queue.Queue() # global
    srcPath  = 'C:\\temp'
    destPath = 'D:\\temp'
    tcnt = 0
    ttotal = 0

    def CopyWorker():
        while True:
            fileName = fileQueue.get()
            fileQueue.task_done()
            shutil.copy(fileName, destPath)
            #tcnt += 1
            print 'copied: ', tcnt, ' of ', ttotal

    def threadWorkerCopy(fileNameList):
        print 'threadWorkerCopy: ', len(fileNameList)
        ttotal = len(fileNameList)
        for i in range(4):
            t = threading.Thread(target=CopyWorker)
            t.daemon = True
            t.start()
        for fileName in fileNameList:
            fileQueue.put(fileName)
        fileQueue.join()

    def sequentialCopy(fileNameList):
        #around 160.446 seconds, 152 seconds
        print 'sequentialCopy: ', len(fileNameList)
        cnt = 0
        ctotal = len(fileNameList)
        for fileName in fileNameList:
            shutil.copy(fileName, destPath)
            cnt += 1
            print 'copied: ', cnt, ' of ', ctotal

    def main():
        print 'this is main method'
        fileCount = 0
        fileList = glob.glob(srcPath + '\\' + '*.csv')
        #sequentialCopy(fileList)
        threadWorkerCopy(fileList)

    if __name__ == '__main__':
        profile.run('main()')

6 个答案:

答案 0 :(得分:10)

当然它比较慢。硬盘驱动器不得不在文件之间不断寻找。您认为多线程会使这项任务更快,这是完全没有道理的。限制速度是指您从磁盘读取数据或将数据写入磁盘的速度,每次从一个文件到另一个文件的搜索都是丢失传输数据所花费的时间。

答案 1 :(得分:1)

  

我认为这更像是一个I / O绑定任务,多线程应该有助于操作速度,我的方法有什么问题?!

  1. 标点符号过多。只有一个。 “?”是合适的。

  2. 你的假设是错误的。多线程有助于CPU绑定(有时)。它永远不会帮助I / O绑定。从不。

  3. 进程中的所有线程必须在一个线程执行I / O时等待。

      

    还是协同工作?!

    没有。

    如果你想做很多I / O,你需要很多进程。

    如果要复制1000个文件,则需要许多进程。每个进程都会复制一些文件。

答案 2 :(得分:1)

我想我可以验证它是磁盘I / O情况。我在我的机器上进行了类似的测试,从一个非常快速的网络服务器复制回自身,我看到只使用上面的代码(4个线程)几乎提高了1:1的速度。我的测试是复制了总计16.5G的4137个文件:

Sequential copy was 572.033 seconds.
Threaded (4) copy was 180.093 seconds.
Threaded (10) copy was 110.155
Threaded (20) copy was 86.745
Threaded (40) copy was 87.761

正如你所看到的那样,当你进入更高和更高的线程数时会出现一些“衰退”,但是在4个线程中我的速度有了很大的提升。我的计算机非常快,网络连接非常快,所以我想我可以放心地假设你达到了I / O限制。

那就说,看看我在这里得到的共鸣:Python multiprocess/multithreading to speed up file copying。我还没有机会尝试这个代码,但gevent可能会更快。

  • 斯潘塞

答案 3 :(得分:0)

作为一个旁边我只是想补充一点,上面的代码有点错误。你应该打电话 fileQueue.task_done()AFTER shutil.copy(fileName,destPath)..否则不会复制最后的文件:)

答案 4 :(得分:0)

存在cpu bounded个应用程序和i/o bounded应用程序,通常,当顺序版本为cpu有界时,您可以从多线程应用程序获得几乎线性的好处。 但是当你受到i / o限制时,你将无法获得任何收益,许多操作系统可以向你显示CPU的“繁忙时间百分比”和“磁盘繁忙时间百分比”,这样你就可以知道你的情况。

但是,由于通常顺序代码不是异步的,您最终获取一个文件,然后等待该文件副本,然后是下一个文件。这样就可以避免操作系统拥有文件列表,并根据表面磁盘位置确定读取请求的优先级。

结论:如果您寻求最高性能,请使用单线程,但使用Async API以允许操作系统更好地安排读取请求。

答案 5 :(得分:0)

仅当通过高延迟TCP连接进行传输时,多线程I / O方法才是有利的,其中TCP窗口将限制单个TCP连接上的吞吐量。在源和目标之间创建多个TCP连接,并在这些连接之间交织文件(这需要多线程),与标准的FTP或NFS副本相比,CAN的性能要好得多,有些类似NetApp的XCP的程序可以做到这一点。如果您的等待时间很短或副本是本地的,那么您可以寻求的唯一效率是解决瓶颈情况下文件系统所做的任何事情(即数百万个文件等),而答案并非如此。你在做什么。