【发布时间】:2013-12-21 09:31:29
【问题描述】:
在script from this answer 的基础上,我有以下场景:一个包含 2500 个大文本文件(每个约 55Mb)的文件夹,所有文件用制表符分隔。基本上是网络日志。
我需要对每个文件的每一行中的第二个“列”进行 md5 哈希处理,将修改后的文件保存在其他地方。源文件位于机械磁盘上,目标文件位于 SSD 上。
脚本处理前 25 个(左右)文件的速度非常快。然后它会减慢速度。根据前 25 个文件,它应该在 2 分钟左右完成所有文件。但是,根据之后的表现,全部完成需要 15 分钟左右。
它在具有 32 Gb RAM 的服务器上运行,并且任务管理器很少显示超过 6 Gb 的正在使用。我将它设置为启动 6 个进程,但内核上的 CPU 使用率很低,很少超过 15%。
为什么会变慢?磁盘读/写问题?垃圾收集器?代码不好?关于如何加快速度的任何想法?
这是脚本
import os
import multiprocessing
from multiprocessing import Process
import threading
import hashlib
class ThreadRunner(threading.Thread):
""" This class represents a single instance of a running thread"""
def __init__(self, fileset, filedirectory):
threading.Thread.__init__(self)
self.files_to_process = fileset
self.filedir = filedirectory
def run(self):
for current_file in self.files_to_process:
# Open the current file as read only
active_file_name = self.filedir + "/" + current_file
output_file_name = "D:/hashed_data/" + "hashed_" + current_file
active_file = open(active_file_name, "r")
output_file = open(output_file_name, "ab+")
for line in active_file:
# Load the line, hash the username, save the line
lineList = line.split("\t")
if not lineList[1] == "-":
lineList[1] = hashlib.md5(lineList[1]).hexdigest()
lineOut = '\t'.join(lineList)
output_file.write(lineOut)
# Always close files after you open them
active_file.close()
output_file.close()
print "\nCompleted " + current_file
class ProcessRunner:
""" This class represents a single instance of a running process """
def runp(self, pid, numThreads, fileset, filedirectory):
mythreads = []
for tid in range(numThreads):
th = ThreadRunner(fileset, filedirectory)
mythreads.append(th)
for i in mythreads:
i.start()
for i in mythreads:
i.join()
class ParallelExtractor:
def runInParallel(self, numProcesses, numThreads, filedirectory):
myprocs = []
prunner = ProcessRunner()
# Store the file names from that directory in a list that we can iterate
file_names = os.listdir(filedirectory)
file_sets = []
for i in range(numProcesses):
file_sets.append([])
for index, name in enumerate(file_names):
num = index % numProcesses
file_sets[num].append(name)
for pid in range(numProcesses):
pr = Process(target=prunner.runp, args=(pid, numThreads, file_sets[pid], filedirectory))
myprocs.append(pr)
for i in myprocs:
i.start()
for i in myprocs:
i.join()
if __name__ == '__main__':
file_directory = "E:/original_data"
processes = 6
threads = 1
extractor = ParallelExtractor()
extractor.runInParallel(numProcesses=processes, numThreads=threads, filedirectory=file_directory)
【问题讨论】:
-
您可能会获得性能提升,因为操作系统会将第一个文件缓存在内存中,因此不会发生磁盘 I/O。您可以通过重新启动服务器轻松检查这一点,并查看处理速度是否减慢。如果您无法重新启动,您应该通过从磁盘读取足够的文件来填充物理内存来填充缓存。如果您具有本地访问权限,则可以简单地仔细聆听磁盘搜索。值得一提的是,对文件执行散列肯定是受磁盘约束的,而不是 CPU,因此在最好的情况下并行执行它是无用的
-
此外,如果您的源文件位于机械磁盘上,那么同时读取它们的 6 个进程可能会大大降低您的速度,尤其是在 I/O 调度非常糟糕的 Windows 上。将源文件移动到 SSD 会发生什么?
-
实际上,如果 I/O 调度是问题(如果您的 CPU 使用率一直很低,这很可能),您应该通过将
numProcesses降低到 1 来提高性能。 -
@Max Noel 我相信源文件(实际上是编译后的字节码)只会被读取一次并保存在磁盘缓存和内存映射文件中(除非字节码真的,真的 大)
-
@MaxNoel 将进程数减少到 1 肯定会加快速度。我想这最终会受到硬盘上有多少读/写磁头的限制?
标签: python performance multiprocessing