摘要:本文闡述了如何將Intel多核工具運(yùn)用到并行化設(shè)計(jì),介紹了他們的應(yīng)用,分析了對(duì)應(yīng)的解決方案。本文對(duì)于Intel多核并行開(kāi)發(fā)具有借鑒意義。
關(guān)鍵詞:多核,Intel工具,并行開(kāi)發(fā)
一.多核并行化目標(biāo)
多核時(shí)代已經(jīng)來(lái)臨,可是軟件仍然運(yùn)行在多核的某一個(gè)核上,沒(méi)有實(shí)現(xiàn)充分利用其他的核的資源。因此,多核并行化的目標(biāo)就是通過(guò)將串行程序進(jìn)行并行化改造,從而使其可以充分利用多核多cpu資源,實(shí)現(xiàn)負(fù)載均衡的,線程安全的,可擴(kuò)展的,運(yùn)行結(jié)果和串行一致的并行程序,從而跟上多核硬件的發(fā)展。而在這個(gè)過(guò)程中,Intel工具發(fā)揮了重要的作用,體現(xiàn)在對(duì)性能的分析,對(duì)程序操作系統(tǒng)級(jí)別潛在錯(cuò)誤的指正和修改,對(duì)系統(tǒng)級(jí)內(nèi)存使用的分析等,都提供了幫助,是多核開(kāi)發(fā)中的不可缺少的工具。本文將計(jì)算素?cái)?shù)為例,從對(duì)其的并行化設(shè)計(jì)中,說(shuō)明Intel多核工具的應(yīng)用。
二.多核并行化總體設(shè)計(jì)
多核的并行化主要分為以下幾個(gè)部分:第一個(gè)部分是對(duì)串行程序的并行性分析,這里用到的工具是Intel vtune performance analyzer。通過(guò)找到程序的熱點(diǎn),從而找到程序中占用cpu時(shí)間最長(zhǎng)的代碼段,進(jìn)行下一步的并行化改造。第二個(gè)部分是對(duì)串行程序的并行化改造。本文采用的方法是OPENMP。第三個(gè)部分是并行后程序的調(diào)試和優(yōu)化。多線程程序存在同步,死鎖,數(shù)據(jù)競(jìng)爭(zhēng)等操作系統(tǒng)級(jí)別問(wèn)題。結(jié)果的運(yùn)行不夠穩(wěn)定。本文采用Intel thread checker對(duì)程序潛在的死鎖和數(shù)據(jù)競(jìng)爭(zhēng)進(jìn)行分析,并對(duì)并行程序進(jìn)行修改。對(duì)于并行程序的優(yōu)化,本文采用的是Intel thread profiler,對(duì)程序的負(fù)載均衡和運(yùn)行時(shí)間等分析,通過(guò)返回到源程序修改,提高程序性能。幾個(gè)部分的關(guān)系圖如下圖所示:
圖 1 并行程序設(shè)計(jì)工體框圖
三.串行系統(tǒng)并行分析
引入的例子是查找素?cái)?shù)的例子。下面是主要的循環(huán)程序段:
For(long number =3;number<=N;number+=2)
{If (TestForPrime(number))
Primes[ number_of_primes++] =number;}
外部循環(huán)一次迭代可能是素?cái)?shù)的數(shù)字。TestForPrime測(cè)試number的因子,檢測(cè)是否該數(shù)是素?cái)?shù)。
對(duì)串行程序進(jìn)行并行化分析,將采用vtune性能分析器。Intel Vtune是性能分析器,主要通過(guò)對(duì)串行程序的數(shù)據(jù)采樣,調(diào)用圖功能和計(jì)數(shù)器管理器對(duì)程序的熱點(diǎn)進(jìn)行分析,熱點(diǎn)即為占用處理器時(shí)間最長(zhǎng)的代碼段,也即為要并行的部分。通過(guò)vtune的calll graph(如圖2所示),可以看到調(diào)用圖中紅色部分即為熱點(diǎn)區(qū)域,隨著箭頭我們可以找到最終的熱點(diǎn)函數(shù)為T(mén)estForPrime(測(cè)試是否為素?cái)?shù)的函數(shù)),從上面的圖表分析可以看出,F(xiàn)indPrimes本身代碼的運(yùn)行時(shí)間很少,不到1%,處理器大部分時(shí)間花在TestForPrime上,TestForPrime被FindPrimes調(diào)用了600000次,占用處理器90%以上的時(shí)間。因此可以通過(guò)多線程并行設(shè)計(jì)TestForPrime,達(dá)到提升性能的目的。
圖 2 vtune調(diào)用圖分析串行程序
四.串行系統(tǒng)并行化改造
并行化改造的方法有三種,第一種是采用基于共享內(nèi)存的OPENMP方式,第二種是采用消息傳遞的MPI方式,第三種是采用Intel最新推出的線程構(gòu)建模塊TBB(thread building blocks)。本文采用openmp方式并行化。
首先,在for循環(huán)前添加了一行編譯器指令,接著將素?cái)?shù)集設(shè)為全局變量,并對(duì)其加鎖,防止其他線程的錯(cuò)誤修改。
#pragma omp parallel for
For (in i = start; i < =end;i+=2)
{ if(TestForPrime(i))
#pragma omp critical
globalPrimes[gPrimesFound++]=I;
}
#pragma omp parallel for這是一個(gè)openmp指令,為for循環(huán)創(chuàng)建的區(qū)域定義多線程,
實(shí)測(cè),性能提升到0.81秒(之前串行時(shí)間是1.21sec)。
五.串行系統(tǒng)調(diào)試和優(yōu)化技術(shù)
利用Intel thread profiler工具可以更快的優(yōu)化線程。它可以識(shí)別影響性能的同步對(duì)象,突出顯示線程工作的負(fù)載不均衡,顯示使用內(nèi)核的數(shù)量,精確定位出現(xiàn)問(wèn)題的源代碼行,支持多種操作系統(tǒng)和編程語(yǔ)言。它可以監(jiān)控的API有線程和進(jìn)程控制的API,包括線程的創(chuàng)建,終止,暫停,恢復(fù)和退出。同步的API包括互斥,臨界區(qū),鎖,信號(hào)量,線程池,計(jì)時(shí)器,消息,APC和事件。阻塞的API,包括睡眠和超時(shí),用戶I/O等。
對(duì)上面的并行程序用Intel profiler進(jìn)行分析如下圖:
圖 3 profiler分析圖
分析圖分為上下兩個(gè)視圖,上面的視圖時(shí)concurrency level 視圖,即并行度視圖。分為四個(gè)部分,包括CL0,CL1,CL2,CL3。CL1對(duì)應(yīng)的橘黃色部分是串行執(zhí)行的部分,綠色表示全部線程執(zhí)行的部分。下面的視圖時(shí)時(shí)間軸上,所有線程的執(zhí)行狀況。黃色表示串行部分,深綠色表示工作狀態(tài),淺綠色表示等待,黃色區(qū)域表示交換所的控制權(quán)。從圖中可以三個(gè)線程的負(fù)載是不均衡的(線程1和線程2有過(guò)多的等待時(shí)間),同時(shí)也因?yàn)槭褂面i而耗費(fèi)了處理器大量時(shí)間(線程1和線程3之間的黃色區(qū)域代表交換鎖的控制權(quán))。重新定位源程序發(fā)現(xiàn),線程1檢查從3到N/2之間的數(shù)是否為素?cái)?shù),線程3檢查從N/2到N之間的數(shù)是否為素?cái)?shù)。因此線程3要 檢查的時(shí)間更多,運(yùn)算時(shí)間也更長(zhǎng)。如果將數(shù)據(jù)集分為8份,間隔的賦給線程1和線程3,則負(fù)載就會(huì)均衡。
平衡線程的工作負(fù)載代碼段如下圖所示:
#pragma omp parallel for schedule (static ,8 )
For (in i = start; i < =end;i+=2)
{ if(TestForPrime(i))
#pragma omp critical
globalPrimes[gPrimesFound++]=I;
}
速度提升到0.66秒
在用圖形工具分析,可以看到負(fù)載已經(jīng)均衡了。但是仍然存在著鎖,并且阻礙程序性能。這里的鎖是數(shù)據(jù)競(jìng)爭(zhēng),一個(gè)線程釋放鎖,而另一個(gè)線程加鎖,頻繁轉(zhuǎn)換而造成性能降低。分析源程序發(fā)現(xiàn),在程序開(kāi)始時(shí),因?yàn)樗財(cái)?shù)較多,因而造成線程1和線程3頻繁更新全局變量globalPrimes和gPrimesFound,就造成了這樣的現(xiàn)象。通過(guò)雙擊定位,profiler發(fā)現(xiàn)的性能瓶頸和分析的結(jié)果一樣。性能瓶頸在兩個(gè)線程的全局變量切換上。
鎖問(wèn)題的解決,本文參考的解決方案是用原子操作來(lái)代替openmp鎖。
#pragma omp parallel for schedule(static 8)
For(int i= start; i< =end;i+=2)
{ if (TestForPrime(i))
globalPrimes[InterlockedIncrement(gPrimesFound)]=i;
}
經(jīng)過(guò)這一步優(yōu)化,速度提升到0.63秒。用profiler重新分析修改后的程序,發(fā)現(xiàn)負(fù)載均衡和死鎖現(xiàn)象都已經(jīng)沒(méi)有了。線程1和線程2對(duì)共享數(shù)據(jù)的訪問(wèn),也會(huì)造成錯(cuò)誤共享。用Intel thead checker進(jìn)行分析,可以清楚的看到錯(cuò)誤共享的數(shù)據(jù)。
修復(fù)錯(cuò)誤共享有幾種方法,可以將錯(cuò)誤共享位于struct內(nèi),移出一個(gè)對(duì)象,使用編譯器指令調(diào)整內(nèi)存分配,也可以使用TBB的cache_ aligned_allocator。本文采用ICC的_declpsec( align(n))編譯器指令解決這個(gè)問(wèn)題。經(jīng)過(guò)這一步優(yōu)化,速度提升到0.61秒。
六.總結(jié)
Intel工具可以有效地幫助開(kāi)發(fā)者進(jìn)行并行設(shè)計(jì)的開(kāi)發(fā),但是從上面的分析中可以看出vtune,checker和profiler工具雖然可以定位程序的性能瓶頸,線程的死鎖和數(shù)據(jù)競(jìng)爭(zhēng),整個(gè)軟件的負(fù)載均衡和同步,同時(shí)也提供了知識(shí)庫(kù)級(jí)的解決方案??墒?,他們的主要作用是輔助性質(zhì)的。他們找到我們通過(guò)人工數(shù)據(jù)調(diào)試不易發(fā)現(xiàn)的錯(cuò)誤,但是錯(cuò)誤的解決仍然需要開(kāi)發(fā)者具備對(duì)多線程編程存在的相關(guān)問(wèn)題的解決方案的了解。比如死鎖的解決,比如負(fù)載不均衡的解決,只有通過(guò)修改并行化設(shè)計(jì)中的不合理的地方,才能真正的解決多核多線程并行化的問(wèn)題。所以,多核并行化需要系統(tǒng)的知識(shí)。但是開(kāi)發(fā)者如果同時(shí)擁有Intel的輔助工具幫忙,那么結(jié)果會(huì)更快速和準(zhǔn)確的展現(xiàn)在開(kāi)發(fā)者面前,從而提高開(kāi)發(fā)速度和開(kāi)發(fā)的正確性。
參考文獻(xiàn)
[1].www.intel.com