为什么这个python多处理脚本在一段时间后会变慢?

前端之家收集整理的这篇文章主要介绍了为什么这个python多处理脚本在一段时间后会变慢?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

script from this answer的基础上,我有以下场景:一个包含2500个大文本文件文件夹(每个约55Mb),所有制表符分隔. Web日志,基本上.

我需要md5哈希每个文件的每一行中的第二个’列’,将修改后的文件保存在别处.源文件位于机械磁盘上​​,目标文件位于SSD上.

该脚本非常快速地处理前25个(左右)文件.然后它减慢了WAY.基于前25个文件,它应该在2分钟左右完成所有文件.但是,根据之后的表现,完成它们需要15分钟(左右).

它运行在具有32 Gb RAM的服务器上,任务管理器很少显示超过6 Gb的使用情况.我已经设置了启动6个进程,但核心上的cpu使用率很低,很少超过15%.

为什么这放慢了?读/写磁盘问题?垃圾收集器?坏代码?有关如何加快速度的任何想法?

这是脚本

  1. import os
  2. import multiprocessing
  3. from multiprocessing import Process
  4. import threading
  5. import hashlib
  6. class ThreadRunner(threading.Thread):
  7. """ This class represents a single instance of a running thread"""
  8. def __init__(self,fileset,filedirectory):
  9. threading.Thread.__init__(self)
  10. self.files_to_process = fileset
  11. self.filedir = filedirectory
  12. def run(self):
  13. for current_file in self.files_to_process:
  14. # Open the current file as read only
  15. active_file_name = self.filedir + "/" + current_file
  16. output_file_name = "D:/hashed_data/" + "hashed_" + current_file
  17. active_file = open(active_file_name,"r")
  18. output_file = open(output_file_name,"ab+")
  19. for line in active_file:
  20. # Load the line,hash the username,save the line
  21. lineList = line.split("\t")
  22. if not lineList[1] == "-":
  23. lineList[1] = hashlib.md5(lineList[1]).hexdigest()
  24. lineOut = '\t'.join(lineList)
  25. output_file.write(lineOut)
  26. # Always close files after you open them
  27. active_file.close()
  28. output_file.close()
  29. print "\nCompleted " + current_file
  30. class ProcessRunner:
  31. """ This class represents a single instance of a running process """
  32. def runp(self,pid,numThreads,filedirectory):
  33. mythreads = []
  34. for tid in range(numThreads):
  35. th = ThreadRunner(fileset,filedirectory)
  36. mythreads.append(th)
  37. for i in mythreads:
  38. i.start()
  39. for i in mythreads:
  40. i.join()
  41. class ParallelExtractor:
  42. def runInParallel(self,numProcesses,filedirectory):
  43. myprocs = []
  44. prunner = ProcessRunner()
  45. # Store the file names from that directory in a list that we can iterate
  46. file_names = os.listdir(filedirectory)
  47. file_sets = []
  48. for i in range(numProcesses):
  49. file_sets.append([])
  50. for index,name in enumerate(file_names):
  51. num = index % numProcesses
  52. file_sets[num].append(name)
  53. for pid in range(numProcesses):
  54. pr = Process(target=prunner.runp,args=(pid,file_sets[pid],filedirectory))
  55. myprocs.append(pr)
  56. for i in myprocs:
  57. i.start()
  58. for i in myprocs:
  59. i.join()
  60. if __name__ == '__main__':
  61. file_directory = "E:/original_data"
  62. processes = 6
  63. threads = 1
  64. extractor = ParallelExtractor()
  65. extractor.runInParallel(numProcesses=processes,numThreads=threads,filedirectory=file_directory)
最佳答案
散列是一项相对简单的任务,与旋转磁盘的速度相比,现代cpu速度非常快. i7上的快速基础测试显示,它可以使用MD5散列大约450 MB / s,使用SHA-1散布大约290 MB / s.相比之下,旋转盘具有约70-150MB / s的典型(顺序原始读取)速度.这意味着,即使忽略文件系统的开销和最终的磁盘搜索,cpu也可以将文件散列大约比磁盘读取速度快3倍.

处理第一个文件时可能会提高性能,因为操作系统会将第一个文件缓存在内存中,因此不会发生磁盘I / O.这可以通过以下任一方式确认:

>重新启动服务器,从而刷新缓存
>通过从磁盘读取足够大的文件,用其他东西填充缓存
>在处理第一个文件时仔细监听磁盘搜索的缺失

现在,由于散列文件性能瓶颈是磁盘,因此在多个进程或线程中执行散列是没用的,因为它们都使用相同的磁盘.正如@Max Noel所提到的,它实际上可以降低性能,因为您将并行读取多个文件,因此您的磁盘必须在文件之间进行搜索.正如他所提到的,性能也将根据您正在使用的操作系统的I / O调度程序而有所不同.

现在,如果您仍在生成数据,那么您有一些可能的解决方案:

>使用更快的磁盘或SSD,如@Max Noel建议的那样.
>从多个磁盘读取 – 在不同的文件系统中或在RAID上的单个文件系统中读取
>在多台计算机上拆分任务(每台计算机有一个或多个磁盘)

但是,如果你想要做的就是散列这2500个文件并且你已经将它们放在一个磁盘上,那么这些解决方案就毫无用处.将它们从磁盘读取到其他磁盘然后执行散列更慢,因为您将读取文件两次,并且您可以尽可能快地读取它们.

最后,根据@yaccz的想法,如果安装了find,xargs和md5sum的cygwin二进制文件,我想你可以避免编写程序执行散列的麻烦.

猜你在找的Python相关文章