多核CPU上多线程文件复制速度远慢于单线程

5 投票
6 回答
7188 浏览
提问于 2025-04-17 08:45

我正在尝试用Python写一个多线程程序,以加快复制不到1000个的.csv文件的速度。但是,运行多线程代码的速度比顺序复制还要慢。我用profile.py来测量代码的运行时间。我确定我一定是哪里做错了,但不太清楚是什么问题。

环境:

  • 四核CPU。
  • 有两个硬盘,一个存放源文件,另一个是目标硬盘。
  • 1000个csv文件,大小从几KB到10MB不等。

方法:

我把所有文件路径放在一个队列里,然后创建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

我认为这是一个输入输出(I/O)密集型的任务,使用多线程应该能加快操作速度,我的想法有什么问题吗?

有的。

  1. 标点符号太多了。只需要一个就行。问号“?”就可以了。

  2. 你的想法是错的。多线程有时对CPU密集型任务有帮助,但对I/O密集型任务是没用的。绝对没用。

在一个进程中,所有线程都得等着,直到其中一个线程完成I/O操作。

那用协程来做这个工作可以吗?

不可以。

如果你想进行大量的I/O操作,就需要很多进程。

比如说你要复制1000个文件,那就需要很多很多进程。每个进程负责复制一部分文件。

2

我觉得可以确认这是一个磁盘输入输出的问题。我在我的电脑上做了一个类似的测试,从一个非常快的网络服务器复制文件到它自己上,我发现使用你上面提供的代码(4个线程)后,速度几乎提高了1倍。我测试的内容是复制4137个文件,总共16.5G:

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个线程的情况下,我的速度提升非常明显。我使用的是一台非常快的电脑,并且网络连接也很快,所以我可以安全地认为你遇到了输入输出的限制。

另外,看看我在这里得到的回复:使用Python的多进程/多线程来加速文件复制。我还没机会试这段代码,但有可能gevent会更快。

  • Spencer
10

当然会慢。硬盘在文件之间不断地寻找位置。你认为多线程会让这个任务更快的想法完全是错误的。限制速度的因素是你从硬盘读取或写入数据的速度,而每次从一个文件跳到另一个文件的过程都会浪费时间,这些时间本可以用来传输数据。

撰写回答