译者 | 布加迪
审校 | 重楼
Python/ target=_blank class=infotextkey>Python历来以使用方便和对程序员友好著称,但它不是市面上速度最快的编程语言。Python的一些速度限制归咎于它的默认实现CPython是单线程的。也就是说,CPython一次只使用一个硬件线程。
虽然您可以使用Python的内置Threading(线程)模块来加快速度,但线程只能提供并发性,而不能提供并行性。它适用于运行不依赖CPU的多个任务,但无助于为每个任务都需要一个完整CPU的多个任务提高速度。这种情况在将来可能会有所改变,而现在,最好假设Python中的线程不会为您提供并行性。
Python包含了一种跨多个CPU运行工作负载的原生方法。Multiprocessing(多处理)模块启动Python解释器的多个副本,每个副本在一个单独的CPU核心上,并提供用于跨核心拆分任务的原语。但有时就连多处理都不够。
在一些情况下,作业不仅需要跨多个核心分配工作,还需要跨多个机器分配工作。这时候本文介绍的Python库和框架就有了用武之地。您可以使用以下七个框架,将现有的Python应用程序及其工作负载分配到多个核心、多台机器或两者之间。
Ray由加州大学伯克利分校的一组研究人员开发,它支持许多分布式机器学习库。但Ray并不仅仅局限于机器学习任务,即使这是它最初的用例。您可以使用Ray在多个系统中分拆和分发任何类型的Python任务。
Ray采用极简语法,所以您不需要对现有的应用程序进行大量的修改就能并行处理它们。@ray.remote装饰器将该函数分布到Ray集群中的任何可用节点上,并可选择指定使用多少个CPU或GPU的参数。每个分布式函数的结果都作为Python对象返回,因此它们易于管理和存储,并且节点间或节点内的复制量最小。比如说,在处理NumPy数组时,最后一项特性就能派得上用场。
Ray甚至包括它自己的内置集群管理器,它可以根据需要在本地硬件或流行的云计算平台上自动启动节点。其他Ray库允许您扩展常见的机器学习和数据科学工作负载,因此您不必手动构建它们。比如说,Ray Tune让您可以为大多数常见的机器学习系统(其中包括PyTorch和TensorFlow)执行大规模的超参数调优操作。
从外表上看,Dask很像Ray。它也是一个用于Python分布式并行计算的库,拥有自己的任务调度系统,支持NumPy等Python数据框架,并且能够从一台机器扩展到多台机器。
Dask与Ray的一个关键区别在于调度机制。Dask使用集中式调度器来处理集群的所有任务。Ray是去中心化的,这意味着每台机器都运行自己的调度器,因此计划任务方面的任何问题都在单个机器、而不是整个集群的层面上加以处理。Dask的任务框架与Python的原生Concurrent. Futures接口协同工作,所以对于那些使用过这个库的人来说,关于作业如何运行的大多数隐喻应该是熟悉的。
Dask有两种基本工作方式。第一种方式是通过并行化的数据结构——本质上是Dask自己版本的NumPy数组、列表或Pandas DataFrame。将这些结构的Dask版本换成默认值,Dask将自动在集群上分配执行。这通常只需要更改导入的名称,但有时可能需要重写才能完全工作。
第二种方式是通过Dask的低级并行化机制(包括函数装饰器),在节点之间分配作业,并同步(“立即”模式下)或异步(“懒惰”模式下)返回结果。这两种模式都可以根据需要混合使用。
Dask还提供了一个名为actor的功能。actor是指向另一个Dask节点上作业的对象。这样一来,需要大量本地状态的作业可以就地运行,并由其他节点远程调用,因此不必复制作业的状态。Ray缺乏像Dask的actor模型这样的机制来支持更复杂的作业分配。然而,Dask的调度器并不知道actor做什么,所以如果actor失控或挂起,调度器无法进行干预。文档是这样描述的:“高性能但不具有弹性”,所以应该谨慎使用actor。
Dispy允许您在机器集群上分配整个Python程序或仅仅单个函数,以便并行执行。它使用平台原生机制进行网络通信,以保持快速高效地运行,因此linux、macOS和windows机器都能同样顺畅地工作。这使得它成为比本文讨论的其他解决方案更通用的解决方案,所以如果您需要不是专门用于加速机器学习任务的解决方案或特定的数据处理框架,那么它值得关注。
Dispy语法在某种程度上类似多处理,因为您显式创建一个集群(其中多处理将让您创建一个进程池),向集群提交工作,然后检索结果。修改作业以使用Dispy可能需要做更多的工作,但是您也可以精确地控制如何分派和返回这些作业。比如说,您可以返回临时或部分完成的结果,将文件作为作业分配过程的一部分来传输,并在传输数据时使用SSL加密。
顾名思义,Pandaral·lel是一种跨多个节点并行化Pandas作业的方法。缺点是Pandaral·lel只适用于Pandas。但是如果您正在使用Pandas,并且只需要一种方法来加速单台计算机上跨多个核心的Pandas作业,Pandaral·lel将专注于处理该任务。
请注意,虽然Pandaral·lel确实在Windows上运行,但它只从在Windows Subsystem for Linux中启动的Python会话运行。Linux和macOS用户可以按原样运行Pandaral·lel。
Ipyparallel是另一个高度专门化的多处理和任务分配系统,专门用于跨集群并行执行Jupyter笔记本代码。已经在Jupyter上工作的项目和团队可以立即开始使用Ipyparallel。
Ipyparallel支持许多并行化代码的方法。简单的有map,它将任何函数应用于序列,并在可用节点上平均分配工作。针对较复杂的工作,您可以装饰特定的函数,以便始终远程运行或并行运行。
Jupyter笔记本支持“魔法命令”,用于只能在笔记本环境中执行的操作。Ipyparallel添加了一些自己的魔法命令。比如说,您可以在任何Python语句前加上%px前缀,以便自动并行化。
Joblib有两个主要的目标:并行运行作业;如果没有任何变化,不重新计算结果。这种效率使得Joblib非常适合科学计算,在科学计算中,可重复的结果是神圣不可侵犯的。Joblib的文档提供了如何使用其所有特性的众多示例。
用于并行化工作的Joblib语法非常简单,它相当于一个装饰器,可用于跨处理器拆分作业,或缓存结果。并行作业可以使用线程或进程。
Joblib为计算作业创建的Python对象提供了一个透明的磁盘缓存。这个缓存不仅可以帮助Joblib避免重复工作(如上所述),还可以用于暂停和恢复长时间运行的作业,或者在崩溃后恢复作业未完成的余下处理。缓存还针对NumPy数组等大型对象进行了智能优化。通过使用numpy.memmap,可以在同一系统上的进程之间在内存中共享数据区域。这一切都使得Joblib对于可能需要很长时间才能完成的工作非常有用,因为您可以避免重做现有工作,并根据需要暂停/恢复。
Joblib没有提供在多台独立计算机上分配作业的方法。从理论上讲,可以使用Joblib的管道做到这一点,但使用另一个直接支持它的框架可能来得更容易。
Parsl的全称是“并行脚本库”,它允许您拿来计算作业后,使用与Python的现有Pool对象大致相同的语法将计算作业拆分到多个系统上。它还允许您将不同的计算任务拼接到多步骤工作流中,这些工作流可以并行运行、按顺序运行,也可以通过map/reduce操作运行。
Parsl允许您执行原生Python应用程序,但也可以通过针对shell的命令运行任何其他外部应用程序。您的Python代码就像普通的Python代码一样编写,除了一个特殊的函数装饰器,它标记了您工作的入口点。作业提交系统也让您可以精细化控制对象在目标上的运行方式,比如每个工作节点(worker)的核心数量、每个工作节点有多少内存、CPU关联控制以及轮询超时的频率等等。
Parsl提供的一项出色特性是一组预构建模板,用于将工作分派给各种高端计算资源。这不仅包括AWS或Kube.NETes集群等主要资源,还包括Blue Waters、ASPIRE 1和Frontera等超级计算资源(假设您有访问权限)。Parsl是在许多制造此类硬件的机构的帮助下共同开发而成的。
Python在线程方面的限制将继续有所改善,主要的变化是允许线程并排运行,以完成受CPU限制的工作。但这些更新离实际可用还有数年时间。为并行性设计的库可以在我们等待的过程中帮助填补这个空白。
原文标题:7 Python libraries for parallel processing,作者:Serdar Yegulalp