【问题标题】:Fastest way to process large files in Python在 Python 中处理大文件的最快方法
【发布时间】:2015-01-22 20:24:31
【问题描述】:

我们需要处理的各个目录中有大约 500GB 的图像。每个图像大小约为 4MB,我们有一个 python 脚本来一次处理每个图像(它读取元数据并将其存储在数据库中)。每个目录可能需要 1-4 小时来处理,具体取决于大小。

我们在 GNU/Linux 操作系统上拥有 2.2Ghz 四核处理器和 16GB RAM。当前脚本仅使用一个处理器。利用其他内核和 RAM 更快地处理图像的最佳方法是什么?启动多个 Python 进程来运行脚本是否会利用其他内核?

另一种选择是使用 Gearman 或 Beanstalk 之类的工具将工作分包给其他机器。我查看了多处理库,但不确定如何使用它。

【问题讨论】:

  • 如果您已经有一个工作脚本可以指向您想要的任何文件/目录,请考虑编写一个 shell 脚本来根据需要启动尽可能多的实例。
  • 我会先看看瓶颈在哪里。如果您将大部分时间花在执行 IO 上,那么您将无法走得更快。例如,如果单个进程设法以一定的速度从磁盘加载数据,那么当多个进程正在咀嚼磁盘时,您就不太可能保持该速度。
  • 我真的同意 Jeff Forster 的观点,首先要寻找瓶颈。如果您对文件所做的唯一处理是读取元数据并存储它们,那么您的瓶颈很可能是磁盘访问。因此,尝试添加更多进程访问同一个磁盘只会降低性能。

标签: python optimization parallel-processing


【解决方案1】:

启动多个 Python 进程来运行脚本会利用其他内核吗?

是的,如果任务受 CPU 限制,它会。这可能是最简单的选择。但是,不要为每个文件或每个目录生成一个进程;考虑使用parallel(1) 之类的工具,并让它在每个内核中生成两个进程。

另一种选择是使用 Gearman 或 Beanstalk 之类的工具将工作外包给其他机器。

这可能行得通。另外,看看Python binding for ZeroMQ,它使分布式处理变得非常容易。

我查看了多处理库,但不确定如何使用它。

定义一个函数,比如process,它读取单个目录中的图像,连接到数据库并存储元数据。让它返回一个指示成功或失败的布尔值。让directories 成为要处理的目录列表。那么

import multiprocessing
pool = multiprocessing.Pool(multiprocessing.cpu_count())
success = all(pool.imap_unordered(process, directories))

将并行处理所有目录。如果需要,您还可以在文件级别进行并行处理;这需要更多的修补。

请注意,这将在第一次失败时停止;让它容错需要更多的工作。

【讨论】:

    【解决方案2】:

    启动独立的 Python 进程是理想的。进程之间不会发生锁争用,操作系统会安排它们同时运行。

    您可能想试验一下,看看理想的实例数是多少 - 它可能多于或少于核心数。磁盘和缓存内存会发生争用,但另一方面,您可能会在另一个进程等待 I/O 时运行一个进程。

    【讨论】:

      【解决方案3】:

      您可以使用多处理池来创建进程以提高性能。假设您有一个用于处理图像的函数 handle_file。如果您使用迭代,它最多只能使用一个核心的 100%。为了利用多核,池多处理为您创建子进程,并将您的任务分配给它们。这是一个例子:

      import os
      import multiprocessing
      
      def handle_file(path):
          print 'Do something to handle file ...', path
      
      def run_multiprocess():
          tasks = []
      
          for filename in os.listdir('.'):
              tasks.append(filename)
              print 'Create task', filename
      
          pool = multiprocessing.Pool(8)
          result = all(list(pool.imap_unordered(handle_file, tasks)))
          print 'Finished, result=', result
      
      def run_one_process():
          for filename in os.listdir('.'):
              handle_file(filename)
      
      if __name__ == '__main__':
          run_one_process
          run_multiprocess()
      

      run_one_process 是处理数据的单核方式,简单但速度慢。另一方面,run_multiprocess 创建 8 个工作进程,并将任务分配给它们。如果你有 8 个核心,它会快 8 倍。我建议您将工作人员数量设置为核心数量的两倍或恰好是核心数量。你可以试试看哪个配置更快。

      对于高级分布式计算,您可以使用ZeroMQ,正如 larsmans 所述。一开始很难理解。但是一旦你理解了它,你就可以设计一个非常高效的分布式系统来处理你的数据。在你的情况下,我认为一个 REQ 和多个 REP 就足够了。

      希望这会有所帮助。

      【讨论】:

        【解决方案4】:

        请参阅answer 至此question

        如果应用程序可以处理输入数据范围,那么您可以启动 4 具有要处理的不同输入数据范围的应用程序实例 并在全部完成后合并结果。

        尽管这个问题看起来是特定于 Windows 的,但它适用于所有操作系统上的单线程程序。

        警告:请注意,此进程将受 I/O 限制,对硬盘驱动器的过多并发访问实际上会导致进程作为一个组执行由于 I/O 资源争用而导致的顺序处理。

        【讨论】:

          【解决方案5】:

          如果您正在读取大量文件并将元数据保存到数据库中,则您的程序不需要更多内核。

          您的进程可能受 IO 限制而非 CPU 限制。使用带适当延迟和回调的扭曲可能会胜过任何寻求获得 4 个核心的解决方案。

          【讨论】:

            【解决方案6】:

            我认为在这种情况下使用Celery 是非常有意义的。

            【讨论】:

              猜你喜欢
              • 2015-07-29
              • 2020-03-16
              • 1970-01-01
              • 2017-08-24
              • 1970-01-01
              • 2011-01-20
              • 2015-02-28
              • 1970-01-01
              • 2015-03-27
              相关资源
              最近更新 更多