新聞中心

EEPW首頁 > 設(shè)計應用 > 多核編程的幾個難題及其應對策略

多核編程的幾個難題及其應對策略

——
作者:周偉明 時間:2007-09-26 來源:CSDN 收藏

   隨著 CPU的出世,方面的問題將擺上了程序員的日程,有許多老的程序員以為早就有多CPU的機器,業(yè)界在多CPU機器上的已經(jīng)積累了很多經(jīng)驗,CPU上的應該差不多,只要借鑒以前的多任務編程、并行編程和并行算法方面的經(jīng)驗就足夠了。

  我想說的是,多核機器和以前的多CPU機器有很大的不同,以前的多CPU機器都是用在特定領(lǐng)域,比如服務器,或者一些可以進行大型并行計算的領(lǐng)域,這些領(lǐng)域很容易發(fā)揮出多CPU的優(yōu)勢,而現(xiàn)在多核機器則是應用到普通用戶的各個層面,特別是客戶端機器要使用多核CPU,而很多客戶端軟件要想發(fā)揮出多核的并行優(yōu)勢恐怕沒有服務器和可以進行大型并行計算的特定領(lǐng)域簡單。

  串行化方面的難題

  1)加速系數(shù)

  衡量多處理器系統(tǒng)的性能時,通常要用到的一個指標叫做加速系數(shù),定義如下:S(p) = 使用單處理器執(zhí)行時間(最好的順序算法)/ 使用具有p個處理器所需執(zhí)行時間

  2)阿姆爾達定律

  并行處理時有一個阿姆爾達定律,用方程式表示如下:

  S(p) = p / (1 + (p-1)*f)

  其中 S(p)表示加速系數(shù)

  p表示處理  
器的個數(shù)

  f表示串行部分所占整個程序執(zhí)行時間的比例

  當f = 5%, p = 20時, S(p) = 10.256左右

  當f = 5%, p = 100時, S(p) = 16.8左右

  也就是說只要有5%的串行部分,當處理器個數(shù)從20個增加到100個時,加速系數(shù)只能從10.256增加到16.8左右,處理器個數(shù)增加了5倍,速度只增加了60%多一點。即使處理器個數(shù)增加到無窮多個,加速系數(shù)的極限值也只有20。

  如果按照阿姆爾達定律的話,可以說多核方面幾乎沒有任何發(fā)展前景,即使軟件中只有1%的不可并行化部分,那么最大加速系統(tǒng)也只能到達100,再多的CPU也無法提升速度性能。按照這個定律,可以說多核CPU的發(fā)展讓摩爾定律延續(xù)不了多少年就會到達極限。

  3)Gustafson定律

  Gustafson提出了和阿姆爾達定律不同的假設(shè)來證明加速系數(shù)是可以超越阿姆爾達定律的限制的,Gustafson認為軟件中的串行部分是固定的,不會隨規(guī)模的增大而增大,并假設(shè)并行處理部分的執(zhí)行時間是固定的(服務器軟件可能就是這樣)。Gustafson定律用公式描述如下:

  S(p) = p + (1-p)*fts

  其中fts表示串行執(zhí)行所占的比例

  如果串行比例為5%,處理器個數(shù)為20個,那么加速系數(shù)為20+(1-20)*5%=19.05

  如果串行比例為5%,處理器個數(shù)為100個,那么加速系數(shù)為100+(1-100)*5%=95.05

  Gustafson定律中的加速系數(shù)幾乎跟處理器個數(shù)成正比,如果現(xiàn)實情況符合Gustafson定律的假設(shè)前提的話,那么軟件的性能將可以隨著處理個數(shù)的增加而增加。

  4)實際情況中的串行化分析

  阿姆爾達定律和Gustafson定律的計算結(jié)果差距如此之大,那么現(xiàn)實情況到底是符合那一個定律呢?我個人認為現(xiàn)實情況中既不會象阿姆爾達定律那么悲觀,但也不會象Gustafson定律那么樂觀。為什么這樣說呢?還是進行一下簡單的分析吧。

  首先需要確定軟件中到底有那么內(nèi)容不能并行化,才能估計出串行部分所占的比例,20世紀60年代時,Bernstein就給出了不能進行并行計算的三個條件:

  條件1:C1寫某一存儲單元后,C2讀該單元的數(shù)據(jù)。稱為“寫后讀”競爭

  條件2:C1讀某一存儲單元數(shù)據(jù)后,C2寫該單元。稱為“讀后寫”競爭

  條件1:C1寫某一存儲單元后,C2寫該單元。稱為“寫后寫”競爭

  滿足以上三個條件中的任何一個都不能進行并行執(zhí)行。不幸的是在實際的軟件中大量存在滿足上述情況的現(xiàn)象,也就是我們常說的共享數(shù)據(jù)要加鎖保護的問題。

  加鎖保護導致的串行化問題如果在任務數(shù)量固定的前提下,串行化所占的比例是隨軟件規(guī)模的增大而減小的,但不幸的是它會隨任務數(shù)量的增加而增加,也就是說處理器個數(shù)越多,鎖競爭導致的串行化將越嚴重,從而使得串行化所占的比例隨處理器個數(shù)的增加而急劇增加。(關(guān)于鎖競爭導致的串行化加劇情況我會在另一篇文章中講解)。所以串行化問題是多核編程面臨的一大難題。

  5)可能的解決措施

  對于串行化方面的難題,首先想到的解決措施就是少用鎖,甚至采用無鎖編程,不過這對普通程序員來說幾乎是難以完成的工作,因為無鎖編程方面的算法太過于復雜,而且使用不當很容易出錯,許多已經(jīng)發(fā)表到專業(yè)期刊上的無鎖算法后來又被證明是錯的,可以想象得到這里面的難度有多大。

  第二個解決方案就是使用原子操作來替代鎖,使用原子操作本質(zhì)上并沒有解決串行化問題,只不過是讓串行化的速度大大提升,從而使得串行化所占執(zhí)行時間比例大大下降。不過目前芯片廠商提供的原子操作很有限,只能在少數(shù)地方起作用,芯片廠商在這方面可能還需要繼續(xù)努力,提供更多功能稍微強大一些的原子操作來避免更多的地方的鎖的使用。 


    第三個解決方案是從設(shè)計和算法層面來縮小串行化所占的比例。也許需要發(fā)現(xiàn)實用的并行方面的設(shè)計模式來縮減鎖的使用,目前業(yè)界在這方面已經(jīng)積累了一定的經(jīng)驗,如任務分解模式,數(shù)據(jù)分解模式,數(shù)據(jù)共享模式,相信隨著多核CPU的大規(guī)模使用將來會有更多的新的有效的并行設(shè)計模式和算法冒出來。

  第四個解決方案是從芯片設(shè)計方面來考慮的,由于我對芯片設(shè)計方面一無所知,所以這個解決方案也許只是我的一廂情愿的猜想。主要的想法是在芯片層面設(shè)計一些新的指令,這些指令不象以前單核CPU指令那樣是由單個CPU完成的,而是由多個CPU進行并行處理完成的一些并行指令,這樣程序員調(diào)用這些并行處理指令編程就象編寫串行化程序一樣,但又充分利用上了多個CPU的優(yōu)勢。

  負載平衡問題

  多核編程中的鎖競爭難題 這篇文章中講過一個多核編程中的串行化的難題,這篇文章中再來講解一下多核編程中的另外一個難題,就是負載平衡方面的難題。

 
  多核CPU中,要很好地發(fā)揮出多個CPU的性能的話,必須保證分配到各個CPU上的任務有一個很好的負載平衡。否則一些CPU在運行,另外一些CPU處于空閑,無法發(fā)揮出多核CPU的優(yōu)勢來。

  要實現(xiàn)一個好的負載平衡通常有兩種方案,一種是靜態(tài)負載平衡,另外一種是動態(tài)負載平衡。

  1、靜態(tài)負載平衡

  靜態(tài)負載平衡中,需要人工將程序分割成多個可并行執(zhí)行的部分,并且要保證分割成的各個部分能夠均衡地分布到各個CPU上運行,也就是說工作量要在多個任務間進行均勻的分配,使得達到高的加速系數(shù)。

  靜態(tài)負載平衡問題從數(shù)學上來說是一個NP完全性問題,Richard M. Karp, Jeffrey D. Ullman, Christos H. Papadimitriou, M. Garey, D. Johnson等人相繼在1972年到1983年間證明了靜態(tài)負載問題在幾種不同約束條件下的NP完全性。

  雖然NP完全性問題在數(shù)學上是難題,但是這并不是標題中所說的難題,因為NP完全性問題一般都可以找到很有效的近似算法來解決。       

  2、動態(tài)負載平衡

  動態(tài)負載平衡是在程序的運行過程中來進行任務的分配達到負載平衡的目的。實際情況中存在許多不能由靜態(tài)負載平衡解決的問題,比如一個大的循環(huán)中,循環(huán)的次數(shù)是由外部輸入的,事先并不知道循環(huán)的次數(shù),此時采用靜態(tài)負載平衡劃分策略就很難實現(xiàn)負載平衡。

  動態(tài)負載平衡中對任務的調(diào)度一般是由系統(tǒng)來實現(xiàn)的,程序員通常只能選擇動態(tài)平衡的調(diào)度策略,不能修改調(diào)度策略,由于實際任務中存在很多的不確定因素,調(diào)度算法無法做得很優(yōu),因此動態(tài)負載平衡有時可能達不到既定的負載平衡要求。

  3、負載平衡的難題在那里?

  負載平衡的難題并不在于負載平衡的程度要達到多少,因為即使在各個CPU上分配的任務執(zhí)行時間存在一些差距,但是隨著CPU核數(shù)的增多總能讓總的執(zhí)行時間下降,從而使加速系數(shù)隨CPU核數(shù)的增加而增加。

  負載平衡的困難之處在于程序中的可并行執(zhí)行塊很多要靠程序員來劃分,當然CPU核數(shù)較少時,比如雙核或4核,這種劃分并不是很困難。但隨著核數(shù)的增加,劃分的粒度將變得越來越細,到了16核以上時,估計程序員要為如何劃分任務而抓狂。比如一段順序執(zhí)行的代碼,放到128核的CPU上運行,要手工劃分成128個任務,其劃分的難度可想而知。

  負載劃分的誤差會隨著CPU核數(shù)的增加而放大,比如一個需要16個時間單位的程序分到4個任務上執(zhí)行,平均每個任務上的負載執(zhí)行時間為4個時間單位,劃分誤差為1個時間單位的話,那么加速系數(shù)變成 16/(4+1)=3.2,是理想情況下加速系數(shù) 4的80%。但是如果放到一個16核CPU上運行的話,如果某個任務的劃分誤差如果為0.5個時間單位的話,那么加速系數(shù)變成16/(1+0.5) = 10.67,只有理想的加速系數(shù)16的66.7%,如果核數(shù)再增加的話,由于誤差的放大,加速系數(shù)相比于理想加速系數(shù)的比例還會下降。

  負載劃分的難題還體現(xiàn)在CPU和軟件的升級上,比如在4核CPU上的負載劃分是均衡的,但到了8核、16核上,負載也許又變得不均衡了。軟件升級也一樣,當軟件增加功能后,負載平衡又會遭到破壞,又需要重新劃分負載使其達到平衡,這樣一來軟件設(shè)計的難度和麻煩大大增加了。

  如果使用了鎖的話,一些看起來是均衡的負載也可能會由于鎖競爭變得不平衡起來,詳細情況請看:http://blog.csdn.net/drzhouweiming/archive/2007/04/10/1559718.aspx 

 4、負載平衡的應對策略

  對于運算量較小的軟件,即使放到單核CPU上運行速度也很快,負載平衡做得差一些并沒有太大影響,實際中負載平衡要考慮的是大運算量和規(guī)模很大的軟件,這些軟件需要在多核上進行負載平衡才能較好地利用多核來提高性能。

  對于大規(guī)模的軟件,負載平衡方面采取的應對策略是發(fā)展劃分并行塊的宏觀劃分方法,從整個軟件系統(tǒng)層面來進行劃分,而不是象傳統(tǒng)的針對某些局部的程序和算法來進行并行分解,因為局部的程序通常都很難分解成幾十個以上的任務來運行。

  另外一個應對策略是在工具層面的,也就是編譯工具能夠協(xié)助人工進行并行塊的分解,并找出良好的分解方案來,這方面Intel已經(jīng)作出了一些努力,但是還需要更多的努力讓工具的功能更強大一些才能應對核數(shù)較多時的情況。

  多核編程中的鎖競爭問題

  在前一篇講解多核編程的幾個難題及其對策(難題一)的文章中提到了鎖競爭會讓串行化隨CPU的核數(shù)增多而加劇的現(xiàn)象,這篇文章就來對多核編程的鎖競爭進行深入的分析。

  為了簡化起見,我們先看一個簡單的情況,假設(shè)有4個對等的任務同時啟動運行,假設(shè)每個任務剛開始時有一個需要鎖保護的操作,耗時為1,每個任務其他部分的耗時為25。這幾個任務啟動運行后的運行情況如下圖所示: 



圖1:對等任務的鎖競爭示意圖

  在上圖中,可以看出第1個任務直接執(zhí)行到結(jié)束,中間沒有等待,第2個任務等待了1個時間單位,第3個任務等待了2個時間單位,第3個任務等待了3個時間單位。

  這樣有3個CPU總計等待了6個時間單位,如果這幾個任務是采用OpenMP里的所有任務都在同一點上進行等待到全部任務執(zhí)行完再向下執(zhí)行時,那么總的運行時間將和第四個任務一樣為29個時間單位,加速系數(shù)為:(1+4



關(guān)鍵詞: 多核 編程

評論


相關(guān)推薦

技術(shù)專區(qū)

關(guān)閉