面向并行處理的七個(gè)Python庫(kù)
譯文譯者 | 布加迪
審校 | 重樓
Python歷來(lái)以使用方便和對(duì)程序員友好著稱,但它不是市面上速度最快的編程語(yǔ)言。Python的一些速度限制歸咎于它的默認(rèn)實(shí)現(xiàn)CPython是單線程的。也就是說(shuō),CPython一次只使用一個(gè)硬件線程。
雖然您可以使用Python的內(nèi)置Threading(線程)模塊來(lái)加快速度,但線程只能提供并發(fā)性,而不能提供并行性。它適用于運(yùn)行不依賴CPU的多個(gè)任務(wù),但無(wú)助于為每個(gè)任務(wù)都需要一個(gè)完整CPU的多個(gè)任務(wù)提高速度。這種情況在將來(lái)可能會(huì)有所改變,而現(xiàn)在,最好假設(shè)Python中的線程不會(huì)為您提供并行性。
Python包含了一種跨多個(gè)CPU運(yùn)行工作負(fù)載的原生方法。Multiprocessing(多處理)模塊啟動(dòng)Python解釋器的多個(gè)副本,每個(gè)副本在一個(gè)單獨(dú)的CPU核心上,并提供用于跨核心拆分任務(wù)的原語(yǔ)。但有時(shí)就連多處理都不夠。
在一些情況下,作業(yè)不僅需要跨多個(gè)核心分配工作,還需要跨多個(gè)機(jī)器分配工作。這時(shí)候本文介紹的Python庫(kù)和框架就有了用武之地。您可以使用以下七個(gè)框架,將現(xiàn)有的Python應(yīng)用程序及其工作負(fù)載分配到多個(gè)核心、多臺(tái)機(jī)器或兩者之間。
1.Ray
Ray由加州大學(xué)伯克利分校的一組研究人員開發(fā),它支持許多分布式機(jī)器學(xué)習(xí)庫(kù)。但Ray并不僅僅局限于機(jī)器學(xué)習(xí)任務(wù),即使這是它最初的用例。您可以使用Ray在多個(gè)系統(tǒng)中分拆和分發(fā)任何類型的Python任務(wù)。
Ray采用極簡(jiǎn)語(yǔ)法,所以您不需要對(duì)現(xiàn)有的應(yīng)用程序進(jìn)行大量的修改就能并行處理它們。@ray.remote裝飾器將該函數(shù)分布到Ray集群中的任何可用節(jié)點(diǎn)上,并可選擇指定使用多少個(gè)CPU或GPU的參數(shù)。每個(gè)分布式函數(shù)的結(jié)果都作為Python對(duì)象返回,因此它們易于管理和存儲(chǔ),并且節(jié)點(diǎn)間或節(jié)點(diǎn)內(nèi)的復(fù)制量最小。比如說(shuō),在處理NumPy數(shù)組時(shí),最后一項(xiàng)特性就能派得上用場(chǎng)。
Ray甚至包括它自己的內(nèi)置集群管理器,它可以根據(jù)需要在本地硬件或流行的云計(jì)算平臺(tái)上自動(dòng)啟動(dòng)節(jié)點(diǎn)。其他Ray庫(kù)允許您擴(kuò)展常見的機(jī)器學(xué)習(xí)和數(shù)據(jù)科學(xué)工作負(fù)載,因此您不必手動(dòng)構(gòu)建它們。比如說(shuō),Ray Tune讓您可以為大多數(shù)常見的機(jī)器學(xué)習(xí)系統(tǒng)(其中包括PyTorch和TensorFlow)執(zhí)行大規(guī)模的超參數(shù)調(diào)優(yōu)操作。
2.Dask
從外表上看,Dask很像Ray。它也是一個(gè)用于Python分布式并行計(jì)算的庫(kù),擁有自己的任務(wù)調(diào)度系統(tǒng),支持NumPy等Python數(shù)據(jù)框架,并且能夠從一臺(tái)機(jī)器擴(kuò)展到多臺(tái)機(jī)器。
Dask與Ray的一個(gè)關(guān)鍵區(qū)別在于調(diào)度機(jī)制。Dask使用集中式調(diào)度器來(lái)處理集群的所有任務(wù)。Ray是去中心化的,這意味著每臺(tái)機(jī)器都運(yùn)行自己的調(diào)度器,因此計(jì)劃任務(wù)方面的任何問(wèn)題都在單個(gè)機(jī)器、而不是整個(gè)集群的層面上加以處理。Dask的任務(wù)框架與Python的原生Concurrent. Futures接口協(xié)同工作,所以對(duì)于那些使用過(guò)這個(gè)庫(kù)的人來(lái)說(shuō),關(guān)于作業(yè)如何運(yùn)行的大多數(shù)隱喻應(yīng)該是熟悉的。
Dask有兩種基本工作方式。第一種方式是通過(guò)并行化的數(shù)據(jù)結(jié)構(gòu)——本質(zhì)上是Dask自己版本的NumPy數(shù)組、列表或Pandas DataFrame。將這些結(jié)構(gòu)的Dask版本換成默認(rèn)值,Dask將自動(dòng)在集群上分配執(zhí)行。這通常只需要更改導(dǎo)入的名稱,但有時(shí)可能需要重寫才能完全工作。
第二種方式是通過(guò)Dask的低級(jí)并行化機(jī)制(包括函數(shù)裝飾器),在節(jié)點(diǎn)之間分配作業(yè),并同步(“立即”模式下)或異步(“懶惰”模式下)返回結(jié)果。這兩種模式都可以根據(jù)需要混合使用。
Dask還提供了一個(gè)名為actor的功能。actor是指向另一個(gè)Dask節(jié)點(diǎn)上作業(yè)的對(duì)象。這樣一來(lái),需要大量本地狀態(tài)的作業(yè)可以就地運(yùn)行,并由其他節(jié)點(diǎn)遠(yuǎn)程調(diào)用,因此不必復(fù)制作業(yè)的狀態(tài)。Ray缺乏像Dask的actor模型這樣的機(jī)制來(lái)支持更復(fù)雜的作業(yè)分配。然而,Dask的調(diào)度器并不知道actor做什么,所以如果actor失控或掛起,調(diào)度器無(wú)法進(jìn)行干預(yù)。文檔是這樣描述的:“高性能但不具有彈性”,所以應(yīng)該謹(jǐn)慎使用actor。
3.Dispy
Dispy允許您在機(jī)器集群上分配整個(gè)Python程序或僅僅單個(gè)函數(shù),以便并行執(zhí)行。它使用平臺(tái)原生機(jī)制進(jìn)行網(wǎng)絡(luò)通信,以保持快速高效地運(yùn)行,因此Linux、macOS和Windows機(jī)器都能同樣順暢地工作。這使得它成為比本文討論的其他解決方案更通用的解決方案,所以如果您需要不是專門用于加速機(jī)器學(xué)習(xí)任務(wù)的解決方案或特定的數(shù)據(jù)處理框架,那么它值得關(guān)注。
Dispy語(yǔ)法在某種程度上類似多處理,因?yàn)槟@式創(chuàng)建一個(gè)集群(其中多處理將讓您創(chuàng)建一個(gè)進(jìn)程池),向集群提交工作,然后檢索結(jié)果。修改作業(yè)以使用Dispy可能需要做更多的工作,但是您也可以精確地控制如何分派和返回這些作業(yè)。比如說(shuō),您可以返回臨時(shí)或部分完成的結(jié)果,將文件作為作業(yè)分配過(guò)程的一部分來(lái)傳輸,并在傳輸數(shù)據(jù)時(shí)使用SSL加密。
4.Pandaral·lel
顧名思義,Pandaral·lel是一種跨多個(gè)節(jié)點(diǎn)并行化Pandas作業(yè)的方法。缺點(diǎn)是Pandaral·lel只適用于Pandas。但是如果您正在使用Pandas,并且只需要一種方法來(lái)加速單臺(tái)計(jì)算機(jī)上跨多個(gè)核心的Pandas作業(yè),Pandaral·lel將專注于處理該任務(wù)。
請(qǐng)注意,雖然Pandaral·lel確實(shí)在Windows上運(yùn)行,但它只從在Windows Subsystem for Linux中啟動(dòng)的Python會(huì)話運(yùn)行。Linux和macOS用戶可以按原樣運(yùn)行Pandaral·lel。
5.Ipyparallel
Ipyparallel是另一個(gè)高度專門化的多處理和任務(wù)分配系統(tǒng),專門用于跨集群并行執(zhí)行Jupyter筆記本代碼。已經(jīng)在Jupyter上工作的項(xiàng)目和團(tuán)隊(duì)可以立即開始使用Ipyparallel。
Ipyparallel支持許多并行化代碼的方法。簡(jiǎn)單的有map,它將任何函數(shù)應(yīng)用于序列,并在可用節(jié)點(diǎn)上平均分配工作。針對(duì)較復(fù)雜的工作,您可以裝飾特定的函數(shù),以便始終遠(yuǎn)程運(yùn)行或并行運(yùn)行。
Jupyter筆記本支持“魔法命令”,用于只能在筆記本環(huán)境中執(zhí)行的操作。Ipyparallel添加了一些自己的魔法命令。比如說(shuō),您可以在任何Python語(yǔ)句前加上%px前綴,以便自動(dòng)并行化。
6.Joblib
Joblib有兩個(gè)主要的目標(biāo):并行運(yùn)行作業(yè);如果沒(méi)有任何變化,不重新計(jì)算結(jié)果。這種效率使得Joblib非常適合科學(xué)計(jì)算,在科學(xué)計(jì)算中,可重復(fù)的結(jié)果是神圣不可侵犯的。Joblib的文檔提供了如何使用其所有特性的眾多示例。
用于并行化工作的Joblib語(yǔ)法非常簡(jiǎn)單,它相當(dāng)于一個(gè)裝飾器,可用于跨處理器拆分作業(yè),或緩存結(jié)果。并行作業(yè)可以使用線程或進(jìn)程。
Joblib為計(jì)算作業(yè)創(chuàng)建的Python對(duì)象提供了一個(gè)透明的磁盤緩存。這個(gè)緩存不僅可以幫助Joblib避免重復(fù)工作(如上所述),還可以用于暫停和恢復(fù)長(zhǎng)時(shí)間運(yùn)行的作業(yè),或者在崩潰后恢復(fù)作業(yè)未完成的余下處理。緩存還針對(duì)NumPy數(shù)組等大型對(duì)象進(jìn)行了智能優(yōu)化。通過(guò)使用numpy.memmap,可以在同一系統(tǒng)上的進(jìn)程之間在內(nèi)存中共享數(shù)據(jù)區(qū)域。這一切都使得Joblib對(duì)于可能需要很長(zhǎng)時(shí)間才能完成的工作非常有用,因?yàn)槟梢员苊庵刈霈F(xiàn)有工作,并根據(jù)需要暫停/恢復(fù)。
Joblib沒(méi)有提供在多臺(tái)獨(dú)立計(jì)算機(jī)上分配作業(yè)的方法。從理論上講,可以使用Joblib的管道做到這一點(diǎn),但使用另一個(gè)直接支持它的框架可能來(lái)得更容易。
7.Parsl
Parsl的全稱是“并行腳本庫(kù)”,它允許您拿來(lái)計(jì)算作業(yè)后,使用與Python的現(xiàn)有Pool對(duì)象大致相同的語(yǔ)法將計(jì)算作業(yè)拆分到多個(gè)系統(tǒng)上。它還允許您將不同的計(jì)算任務(wù)拼接到多步驟工作流中,這些工作流可以并行運(yùn)行、按順序運(yùn)行,也可以通過(guò)map/reduce操作運(yùn)行。
Parsl允許您執(zhí)行原生Python應(yīng)用程序,但也可以通過(guò)針對(duì)shell的命令運(yùn)行任何其他外部應(yīng)用程序。您的Python代碼就像普通的Python代碼一樣編寫,除了一個(gè)特殊的函數(shù)裝飾器,它標(biāo)記了您工作的入口點(diǎn)。作業(yè)提交系統(tǒng)也讓您可以精細(xì)化控制對(duì)象在目標(biāo)上的運(yùn)行方式,比如每個(gè)工作節(jié)點(diǎn)(worker)的核心數(shù)量、每個(gè)工作節(jié)點(diǎn)有多少內(nèi)存、CPU關(guān)聯(lián)控制以及輪詢超時(shí)的頻率等等。
Parsl提供的一項(xiàng)出色特性是一組預(yù)構(gòu)建模板,用于將工作分派給各種高端計(jì)算資源。這不僅包括AWS或Kubernetes集群等主要資源,還包括Blue Waters、ASPIRE 1和Frontera等超級(jí)計(jì)算資源(假設(shè)您有訪問(wèn)權(quán)限)。Parsl是在許多制造此類硬件的機(jī)構(gòu)的幫助下共同開發(fā)而成的。
結(jié)論
Python在線程方面的限制將繼續(xù)有所改善,主要的變化是允許線程并排運(yùn)行,以完成受CPU限制的工作。但這些更新離實(shí)際可用還有數(shù)年時(shí)間。為并行性設(shè)計(jì)的庫(kù)可以在我們等待的過(guò)程中幫助填補(bǔ)這個(gè)空白。
原文標(biāo)題:7 Python libraries for parallel processing,作者:Serdar Yegulalp