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