Python multiprocess/multithreading to speed up file copying
我有一个程序可以将大量文件从一个位置复制到另一个位置-我说的是100000多个文件(目前我正在复制314G的图像序列)。他们都在进行大规模的、非常快速的网络存储突袭。我正在使用shutil按顺序复制文件,这需要一些时间,所以我正在尝试找到最佳的方法来对此进行优化。我注意到一些软件,我使用有效的多线程从网络中读取文件,在加载时间上有很大的提高,所以我想尝试在Python中这样做。
我没有编程多线程/多处理的经验-这似乎是正确的继续?如果是这样,最好的方法是什么?我看过其他一些关于用python复制线程文件的文章,它们似乎都说你没有速度提升,但是考虑到我的硬件,我不认为会出现这种情况。目前,我离我的IO容量还差得很远,资源大约占1%(我在本地有40个内核和64G的RAM)。
- 斯宾塞
更新:
我从来没有得到gevent工作(第一个答案),因为我无法安装没有互联网连接的模块,这是我的工作站上没有的。不过,我只使用了内置的python线程就可以将文件复制时间减少8次(我已经学会了如何使用它),我想把它作为其他感兴趣的人的回答张贴出来!下面是我的代码,请注意,由于您的硬件/网络设置,我的8倍复制时间很可能因环境而异。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | import Queue, threading, os, time import shutil fileQueue = Queue.Queue() destPath = 'path/to/cop' class ThreadedCopy: totalFiles = 0 copyCount = 0 lock = threading.Lock() def __init__(self): with open("filelist.txt","r") as txt: #txt with a file per line fileList = txt.read().splitlines() if not os.path.exists(destPath): os.mkdir(destPath) self.totalFiles = len(fileList) print str(self.totalFiles) +" files to copy." self.threadWorkerCopy(fileList) def CopyWorker(self): while True: fileName = fileQueue.get() shutil.copy(fileName, destPath) fileQueue.task_done() with self.lock: self.copyCount += 1 percent = (self.copyCount * 100) / self.totalFiles print str(percent) +" percent copied." def threadWorkerCopy(self, fileNameList): for i in range(16): t = threading.Thread(target=self.CopyWorker) t.daemon = True t.start() for fileName in fileNameList: fileQueue.put(fileName) fileQueue.join() ThreadedCopy() |
这可以通过在python中使用gevent来并行化。
我建议使用以下逻辑来加速100k+文件复制:
将需要复制到csv文件中的所有100k+文件的名称,例如:"input.csv"。
然后从那个csv文件创建块。块的数量应该根据机器中处理器/核心的数量来决定。
将这些块传递给各个线程。
每个线程依次读取该块中的文件名,并将其从一个位置复制到另一个位置。
下面是python代码段:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | import sys import os import multiprocessing from gevent import monkey monkey.patch_all() from gevent.pool import Pool def _copyFile(file): # over here, you can put your own logic of copying a file from source to destination def _worker(csv_file, chunk): f = open(csv_file) f.seek(chunk[0]) for file in f.read(chunk[1]).splitlines(): _copyFile(file) def _getChunks(file, size): f = open(file) while 1: start = f.tell() f.seek(size, 1) s = f.readline() yield start, f.tell() - start if not s: f.close() break if __name__ =="__main__": if(len(sys.argv) > 1): csv_file_name = sys.argv[1] else: print"Please provide a csv file as an argument." sys.exit() no_of_procs = multiprocessing.cpu_count() * 4 file_size = os.stat(csv_file_name).st_size file_size_per_chunk = file_size/no_of_procs pool = Pool(no_of_procs) for chunk in _getChunks(csv_file_name, file_size_per_chunk): pool.apply_async(_worker, (csv_file_name, chunk)) pool.join() |
将文件另存为file_copier.py。打开终端并运行:
1 | $ ./file_copier.py input.csv |