序言
第1章 并行和分布式計算介紹
第2章 異步編程
第3章 Python的并行計算
第4章 Celery分布式應(yīng)用
第5章 云平臺部署Python
第6章 超級計算機(jī)群使用Python
第7章 測試和調(diào)試分布式應(yīng)用
第8章 繼續(xù)學(xué)習(xí)
本書示例代碼適用于Python 3.5及以上。
當(dāng)代第一臺數(shù)字計算機(jī)誕生于上世紀(jì)30年代末40年代初(Konrad Zuse 1936年的Z1存在爭議)沸版,也許比本書大多數(shù)讀者都要早顷歌,比作者本人也要早。過去的七十年見證了計算機(jī)飛速地發(fā)展奄容,計算機(jī)變得越來越快、越來越便宜,這在整個工業(yè)領(lǐng)域中是獨(dú)一無二的。如今的手機(jī)巧骚,iPhone或是安卓,比20年前最快的電腦還要快格二。而且劈彪,計算機(jī)變得越來越小:過去的超級計算機(jī)能裝下整間屋子顶猜,現(xiàn)在放在口袋里就行了沧奴。
這其中包括兩個重要的發(fā)明。其一是主板上安裝多塊處理器(每個處理器含有多個核心)驶兜,這使得計算機(jī)能真正地實(shí)現(xiàn)并發(fā)扼仲。我們知道远寸,一個處理器同一時間只能處理同一事務(wù)抄淑;后面章節(jié)我們會看到,當(dāng)處理器快到一定程度驰后,就可以給出同一時間進(jìn)行多項(xiàng)任務(wù)的假象肆资。若要真正實(shí)現(xiàn)同一時間多任務(wù),就需要多個處理器灶芝。
另一項(xiàng)發(fā)明是高速計算機(jī)網(wǎng)絡(luò)郑原。它首次讓無窮多的電腦實(shí)現(xiàn)了相互通訊。聯(lián)網(wǎng)的電腦可能處于同一地點(diǎn)(稱為局域網(wǎng)LAN)或分布在不同地點(diǎn)(稱為廣域網(wǎng)WAN)夜涕。
如今犯犁,我們都已熟悉多處理器/多核心計算機(jī),事實(shí)上女器,我們的手機(jī)酸役、平板電腦、筆記本電腦都是多核心的。顯卡涣澡,或圖形處理器(GPU)贱呐,往往是大規(guī)模并行機(jī)制,含有數(shù)百乃至上千個處理單元入桂。我們周圍的計算機(jī)網(wǎng)絡(luò)無處不在奄薇,包括:Internet、WiFi抗愁、4G網(wǎng)絡(luò)馁蒂。
本章剩余部分會探討一些定義。我們會介紹并行和分布式計算的概念驹愚。給出一些常見的示例远搪。探討每個架構(gòu)的優(yōu)缺點(diǎn),和編程的范式逢捺。
在開始介紹概念之前谁鳍,先澄清一些東西。在剩余部分中劫瞳,除非特別指明倘潜,我們會交叉使用處理器和CPU核心。這在概念上顯然是不對的:一個處理器會有一個或多個核志于,每臺計算機(jī)會有一個或多個處理器涮因。取決于算法和性能要求,在多處理器或單處理器多核的計算機(jī)上運(yùn)行可能會有速度上的不同伺绽,假定算法是并行的养泡。然而,我們會忽略這些差異奈应,將注意力集中于概念本身澜掩。
并行計算
并行計算的概念很多。本書提供一個簡潔的概念:
并行計算是同時使用多個處理器處理事務(wù)杖挣。
典型的肩榕,這個概念要求這些處理器位于同一塊主板,以區(qū)別于分布式計算惩妇。
分工的歷史和人類文明一樣久遠(yuǎn)株汉,也適用于數(shù)字世界,當(dāng)代計算機(jī)安裝的計算單元越來越多歌殃。
并行計算是有用且必要的原因很多乔妈。最簡單的原因是性能;如果我們要把一個冗長的計算分成小塊氓皱、打包給不同的處理器路召,就可以在相同時間內(nèi)完成更多工作贮懈。
或者,并行計算在處理一項(xiàng)任務(wù)時优训,還可以向用戶呈現(xiàn)反饋界面朵你。記住一個處理器同一時間只能處理一項(xiàng)任務(wù)。有GUIs的應(yīng)用需要將任務(wù)交付給另一個處理器的獨(dú)立線程揣非,以讓另一個處理器能更新GUI抡医,并對輸入進(jìn)行反饋。
下圖展示了這個常見的架構(gòu)早敬,主線程使用事件循環(huán)(Event Loop)處理用戶和系統(tǒng)輸入忌傻。需要長時間處理的任務(wù)和會阻塞GUI的任務(wù)會被移交給后臺或worker線程:
一個該并行架構(gòu)的實(shí)際案例可以是一個圖片應(yīng)用。當(dāng)我們將數(shù)碼相機(jī)或手機(jī)連接到電腦上時搞监,圖片應(yīng)用會進(jìn)行一系列動作水孩,同時它的用戶界面要保持交互。例如琐驴,應(yīng)用要將圖片從設(shè)備拷貝到硬盤上俘种、建立縮略圖、提取元數(shù)據(jù)(拍攝日期及時間)绝淡、生成索引宙刘、最后更新圖片庫。與此同時牢酵,我們?nèi)匀豢梢詾g覽以前傳輸?shù)膱D片悬包,打開圖片、進(jìn)行編輯等等馍乙。
當(dāng)然布近,整個過程在單處理器上可能是順序依次進(jìn)行的,這個處理器也要處理GUI丝格。這就會造成用戶界面反應(yīng)遲緩撑瞧,整個應(yīng)用會變慢。并行運(yùn)行可以使這個過程流暢铁追,提高用戶體驗(yàn)季蚂。
敏銳的讀者此時可能指出茫船,以前的只有單處理器單核的舊電腦也可以(通過多任務(wù))同時處理多個事件琅束。即使如今,也可以讓運(yùn)行的任務(wù)數(shù)超過計算機(jī)的處理器數(shù)目算谈。其實(shí)涩禀,這是因?yàn)橐粋€正在運(yùn)行的任務(wù)被從CPU移出(這可能是自發(fā)或被操作系統(tǒng)強(qiáng)制的,例如然眼,響應(yīng)IO事件)艾船,好讓另一個任務(wù)可以在CPU上運(yùn)行。類似的中斷會時而發(fā)生,在應(yīng)用運(yùn)行中屿岂,各種任務(wù)會相繼獲得會被移出CPU践宴。切換通常很快,這樣爷怀,用戶就會有計算機(jī)并行運(yùn)行任務(wù)的感覺阻肩。實(shí)際上,只有一個任務(wù)在特定的時間運(yùn)行运授。
通常在并行應(yīng)用中運(yùn)行的工具是線程烤惊。系統(tǒng)(比如Python)通常對線程有嚴(yán)格的限制(見第3章),開發(fā)者要轉(zhuǎn)而使用子進(jìn)程subprocess(通常的方法是分叉)吁朦。子進(jìn)程取代(或配合)線程柒室,與主進(jìn)程同時運(yùn)行。
第一種方法是多線程編程(multithreaded programming)逗宜。第二種方法是多進(jìn)程(multiprocessing)雄右。多進(jìn)程可以看做是多線程的變通。
許多情況下纺讲,多進(jìn)程要優(yōu)于多線程不脯。有趣的是,盡管二者都在單機(jī)運(yùn)行刻诊,多線程是共享內(nèi)存架構(gòu)的例子防楷,而多進(jìn)程是分布式內(nèi)存架構(gòu)的例子(參考本書后續(xù)內(nèi)容)。
分布式計算
本書采用如下對分布式計算的定義:
分布式計算是指同一時間使用多臺計算機(jī)處理一個任務(wù)则涯。
一般的复局,與并行計算類似,這個定義也有限制粟判。這個限制通常是要求亿昏,對于使用者,這些計算機(jī)可以看做一臺機(jī)器档礁,進(jìn)而掩蓋應(yīng)用的分布性角钩。本書中,我們更喜歡這個廣義的定義呻澜。
顯然递礼,只有當(dāng)計算機(jī)之間互相連接時,才可以使用分布式計算羹幸。事實(shí)上脊髓,許多時候,這只是對我們在之前部分的并行計算的概念總結(jié)栅受。
搭建分布式系統(tǒng)的理由有很多将硝。通常的原因是恭朗,要做的任務(wù)量太大,一臺計算機(jī)難以完成依疼,或是不能在一定時間內(nèi)完成痰腮。一個實(shí)際的例子就是皮克斯或夢工廠的3D動畫電影渲染。
考慮到整部電影要渲染的總幀數(shù)(電影兩個小時律罢,每秒有30幀)诽嘉,電影工作室需要將海量的工作分配到多臺計算機(jī)(他們稱其為計算機(jī)農(nóng)場)。
另外弟翘,應(yīng)用本身需要分布式的環(huán)境虫腋。例如,即時聊天和視頻會議應(yīng)用稀余。對于這些應(yīng)用悦冀,性能不是最重要的。最關(guān)鍵的是睛琳,應(yīng)用本身要是分布式的盒蟆。下圖中,我們看到一個非常常見的網(wǎng)絡(luò)應(yīng)用架構(gòu)(另一個分布式應(yīng)用例子)师骗,多個用戶與網(wǎng)站相連历等。同時,應(yīng)用本身要與LAN中不同主機(jī)的系統(tǒng)(例如數(shù)據(jù)庫服務(wù)器)通訊:
另一個分布式系統(tǒng)的例子辟癌,可能有點(diǎn)反直覺寒屯,就是CPU-GPU。如今黍少,顯卡本身就是很復(fù)雜的計算機(jī)寡夹。它們高并行運(yùn)行,處理海量計算密集型任務(wù)厂置,不僅是為了在顯示器上顯示圖像菩掏。有大量的工具和庫(例如NVIDIA的CUDA,OpenCL和OpenAcc)可以讓開發(fā)者對GPU進(jìn)行開發(fā)昵济,來做廣義計算任務(wù)智绸。(譯者注:比如在比特幣中,使用顯卡編程來挖礦访忿。)
然而瞧栗,CPU和GPU組成的系統(tǒng)實(shí)際上就是一個分布式系統(tǒng),網(wǎng)絡(luò)被PCI總線取代了醉顽。任何要使用CPU和GPU的應(yīng)用都要處理數(shù)據(jù)在兩個子系統(tǒng)之間的移動沼溜,就像傳統(tǒng)的在網(wǎng)絡(luò)中運(yùn)行的應(yīng)用平挑。
將現(xiàn)存的代碼移植到計算機(jī)網(wǎng)絡(luò)(或GPU)不是一件輕松的工作游添。要移植的話系草,我發(fā)現(xiàn)先在一臺計算機(jī)上使用多進(jìn)程完成,是一個很有用的中間步驟唆涝。我們會在第3章看到找都,Python有強(qiáng)大的功能完成這項(xiàng)任務(wù)(參考concurrent.futures
模塊)。
一旦完成多進(jìn)程并行運(yùn)行廊酣,就可以考慮將這些進(jìn)程分拆給獨(dú)立的應(yīng)用能耻,這就不是重點(diǎn)了。
特別要注意的是數(shù)據(jù)亡驰,在哪里存儲晓猛、如何訪問。簡單情況下凡辱,共享式的文件系統(tǒng)(例如戒职,UNIX的NFS)就足夠了;其余情況透乾,就需要數(shù)據(jù)庫或是消息隊(duì)列洪燥。我們會在第4章中看幾個這樣的實(shí)例。要記住乳乌,真正的瓶頸往往是數(shù)據(jù)而不是CPU捧韵。
共享式內(nèi)存vs分布式內(nèi)存
在概念上,并行計算和分布計算很像汉操,畢竟再来,二者都是要將總計算量分解成小塊,再在處理器上運(yùn)行磷瘤。有些讀者可能會想其弊,一種情況下,使用的處理器全部位于一臺計算機(jī)之內(nèi)膀斋,另一種情況下梭伐,處理器位于不同的計算機(jī)。那么仰担,這種技術(shù)是不是有點(diǎn)多余糊识?
答案是,可能摔蓝。正如我們看到的赂苗,一些應(yīng)用本身是分布式的。其它應(yīng)用則需要更多的性能贮尉。對于這些應(yīng)用拌滋,答案就可能是“有點(diǎn)多余”——應(yīng)用本身不關(guān)心算力來自何處。然而猜谚,考慮到所有情況败砂,硬件資源的物理存放地點(diǎn)還是有一定意義的赌渣。
也許,并行和分布式計算的最明顯的差異就是底層的內(nèi)存架構(gòu)和訪問方式不同昌犹。對于并行計算坚芜,原則上,所有并發(fā)任務(wù)可以訪問同一塊內(nèi)存空間斜姥。這里鸿竖,我們必須要說原則上,因?yàn)槲覀円呀?jīng)看到并行不一定非要用到線程(線程是可以訪問同一塊內(nèi)存空間)铸敏。
下圖中缚忧,我們看到一個典型的共享式內(nèi)存架構(gòu),四個處理器可以訪問同一內(nèi)存地址杈笔。如果應(yīng)用使用線程搔谴,如果需要的話,線程就可以訪問同一內(nèi)存空間:
然而,對于分布式應(yīng)用,不同的并發(fā)任務(wù)不能正常訪問同一內(nèi)存流酬。原因是榕莺,一些任務(wù)是在這一臺計算機(jī)運(yùn)行,一些任務(wù)是在另一臺計算機(jī)運(yùn)行,它們是物理分隔的。
因?yàn)橛嬎銠C(jī)之間可以靠網(wǎng)絡(luò)通訊,可以想象寫一個軟件層(中間件)右钾,以一個統(tǒng)一的內(nèi)存邏輯空間呈現(xiàn)應(yīng)用。這些中間件就是分布式共享內(nèi)存架構(gòu)旱爆。此書不涉及這樣的系統(tǒng)舀射。
下圖中,我們還有有四個CPU怀伦,它們處于共享內(nèi)存架構(gòu)中脆烟。每個CPU都有各自的私有內(nèi)存,看不到其它CPU的內(nèi)存空間房待。四臺計算機(jī)(包圍CPU和內(nèi)存的方框)通過網(wǎng)絡(luò)通訊邢羔,通過網(wǎng)絡(luò)進(jìn)行數(shù)據(jù)傳輸:
現(xiàn)實(shí)中,計算機(jī)是我們之前講過的兩種極端情況的結(jié)合體桑孩。計算機(jī)網(wǎng)絡(luò)通訊就像一個純粹的分布式內(nèi)存架構(gòu)拜鹤。然而,每臺計算機(jī)有多個處理器流椒,運(yùn)行著共享式內(nèi)存架構(gòu)敏簿。下圖描述了這樣的混合式架構(gòu):
這些架構(gòu)有各自的優(yōu)缺點(diǎn)。對于共享式內(nèi)存系統(tǒng),在單一文件的并發(fā)線程中分享數(shù)據(jù)速度極快惯裕,遠(yuǎn)遠(yuǎn)快過網(wǎng)絡(luò)傳輸温数。另外,使用單一內(nèi)存地址可以簡化編程轻猖。
同時帆吻,還要注意不要讓各個線程發(fā)生重疊域那,或是彼此改變參數(shù)咙边。
分布式內(nèi)存系統(tǒng)擴(kuò)展性強(qiáng)、組建成本低:需要更高性能次员,擴(kuò)展即可败许。另一優(yōu)點(diǎn)是,處理器可以訪問各自的內(nèi)存淑蔚,不必?fù)?dān)心發(fā)生競爭條件(競爭條件指多個線程或者進(jìn)程在讀寫一個共享數(shù)據(jù)時市殷,結(jié)果依賴于它們執(zhí)行的相對時間的情形)。它的缺點(diǎn)是刹衫,開發(fā)者需要手動寫數(shù)據(jù)傳輸?shù)牟呗源浊蓿枰紤]數(shù)據(jù)存儲的位置。另外带迟,不是所有算法都能容易移植到這種架構(gòu)音羞。
阿姆達(dá)爾定律
本章最后一個重要概念是阿姆達(dá)爾定律。簡言之仓犬,阿姆達(dá)爾定律是說嗅绰,我們可以盡情并行化或分布化計算,添加算力資源獲得更高性能搀继。然而窘面,最終代碼的速度不能比運(yùn)行在單處理器的單序列(即非并行)的組件要快。
更正式的叽躯,阿姆達(dá)爾定律有如下公式财边。考慮一個部分并行的算法点骑,稱P
為并行分量制圈,S
為序列分量(即非并行分量),P+S=100%
畔况。T(n)
為運(yùn)行時間鲸鹦,處理器數(shù)量為n
。有如下關(guān)系:
這個公式轉(zhuǎn)化成白話就是:在n
個處理器上運(yùn)行這個算法的時間大于等于跷跪,單處理器上運(yùn)行序列分量的時間S*T(1)
加上馋嗜,并行分量在單處理器上運(yùn)行的時間P*T(1)
除以n
。
當(dāng)提高處理器的數(shù)量n
吵瞻,等式右邊的第二項(xiàng)變得越來越小葛菇,與第一項(xiàng)對比甘磨,逐漸變得可以忽略不計。此時眯停,這個公式近似于:
這個公式轉(zhuǎn)化成白話就是:在無限個處理器上運(yùn)行這個算法的時間近似等于序列分量在單處理器上的運(yùn)行時間S*T(1)
济舆。
現(xiàn)在,思考一下阿姆達(dá)爾定律的意義莺债。此處的假定過于簡單滋觉,通常,我們不能使算法完全并行化齐邦。
也就是說椎侠,大多情況下,我們不能讓S=0
措拇。原因有很多:我們可能必須要拷貝數(shù)據(jù)和/或代碼到不同的處理器可以訪問的位置我纪。我們可能必須要分隔數(shù)據(jù),將數(shù)據(jù)塊在網(wǎng)絡(luò)中傳輸丐吓∏诚ぃ可能要收集所有并發(fā)任務(wù)的結(jié)果,并進(jìn)行進(jìn)一步處理券犁,等等术健。
無論原因是什么,如果不能使算法完全并行化族操,最終的運(yùn)行時間取決于序列分量的表現(xiàn)苛坚。并行化的程度越高,加速表現(xiàn)越不明顯色难。
題外話泼舱,完全并行通常稱作密集并行(Embarrassingly parallel),或者用政治正確的話來說枷莉,愉悅并行(pleasantly parallel)娇昙,它擁有最佳的擴(kuò)展性能(速度與處理器的數(shù)量呈線性關(guān)系)。當(dāng)然笤妙,對此不必感到尷尬冒掌!不幸的是,密集并行很少見蹲盘。
讓我們給阿姆達(dá)爾定律添加一些數(shù)字股毫。假定,算法在單處理器耗時100秒召衔。再假設(shè)铃诬,并行分量為99%,這個數(shù)值已經(jīng)很高了。添加處理器的數(shù)量以提高速度趣席。來看以下計算:
我們看到兵志,隨著n
的提高,加速的效果不讓人滿意宣肚。使用10個處理器想罕,是9.2倍速。使用100個處理器霉涨,則是50倍速按价。使用1000個處理器,僅僅是91倍速嵌纲。
下圖描述了倍速與處理器數(shù)量的關(guān)系俘枫。無論使用多少處理器腥沽,也無法大于100倍速逮走,即運(yùn)行時間小于1秒,即小于序列分量運(yùn)行的時間今阳。
阿姆達(dá)爾定律告訴我們兩點(diǎn):我們最快可以將倍速提高到多少师溅;收益減少時,何時應(yīng)該減少硬件資源的投入盾舌。
另一有趣的地方是阿姆達(dá)爾定律適用于分布式系統(tǒng)和混合并行-分布式系統(tǒng)墓臭。這時,n
等于所有計算機(jī)的處理器總數(shù)目妖谴。
隨著能接觸的系統(tǒng)的性能變得越來越高窿锉,如果能使用剩余性能,還可以縮短分布式算法運(yùn)行的時間膝舅。
隨著應(yīng)用的的執(zhí)行時間變短嗡载,我們就傾向于處理更復(fù)雜的問題。對于這樣的算法進(jìn)化仍稀,即問題規(guī)模的擴(kuò)大(計算要求的擴(kuò)大)達(dá)到可接受的性能時洼滚,稱作古斯塔夫森定律。
混合范式
我們現(xiàn)在能買到的電腦大多是多處理器多核的技潘,我們將要寫的分布式應(yīng)用就是要這樣的電腦上運(yùn)行遥巴。這使得我們可以既開發(fā)分布式計算,也可以開發(fā)并行式計算享幽。這種混合分布-并行范式是如今開發(fā)網(wǎng)絡(luò)分布應(yīng)用的事實(shí)標(biāo)準(zhǔn)〔現(xiàn)實(shí)通常是混合的。
總結(jié)
這一章講了基礎(chǔ)概念值桩。我們學(xué)習(xí)了并行和分布式計算摆霉,以及兩個架構(gòu)的例子,探討了優(yōu)缺點(diǎn)。分析了它們是如何訪問內(nèi)存斯入,并指出現(xiàn)實(shí)通常是混合的砂碉。最后講了阿姆達(dá)爾定律,它對擴(kuò)展性能的意義刻两,硬件投入的經(jīng)濟(jì)考量增蹭。下一章會將概念轉(zhuǎn)化為實(shí)踐,并寫Python代碼磅摹!
序言
第1章 并行和分布式計算介紹
第2章 異步編程
第3章 Python的并行計算
第4章 Celery分布式應(yīng)用
第5章 云平臺部署Python
第6章 超級計算機(jī)群使用Python
第7章 測試和調(diào)試分布式應(yīng)用
第8章 繼續(xù)學(xué)習(xí)