徐海坤,匡鄧暉,劉 杰,2,龔春葉,2
(1.國(guó)防科技大學(xué)并行與分布處理國(guó)家重點(diǎn)實(shí)驗(yàn)室,湖南 長(zhǎng)沙 410073;2.國(guó)防科技大學(xué)復(fù)雜系統(tǒng)軟件工程湖南省重點(diǎn)實(shí)驗(yàn)室,湖南 長(zhǎng)沙 410073)
在核反應(yīng)堆設(shè)計(jì)、臨界安全分析及屏蔽計(jì)算中,中子輸運(yùn)問(wèn)題是數(shù)值計(jì)算核心。通常采用確定論方法和蒙特卡羅MC(Monte Carlo)方法(又稱為隨機(jī)模擬法)[1,2]進(jìn)行數(shù)值模擬。其中基于組合幾何的蒙特卡羅MC方法具有幾何仿真能力強(qiáng)、物理建模完善、模擬近似程度小、對(duì)問(wèn)題維度不敏感等優(yōu)點(diǎn)。但是,利用蒙特卡羅方法進(jìn)行數(shù)值模擬時(shí)也有明顯缺點(diǎn),例如,收斂速度慢,模擬時(shí)間長(zhǎng)[3 - 5]。隨著現(xiàn)代超級(jí)計(jì)算機(jī)技術(shù)的飛速發(fā)展,蒙特卡羅方法天然的粒子并行性使得這些缺點(diǎn)得以明顯改善,但在超大規(guī)模的核反應(yīng)堆數(shù)值模擬計(jì)算中,模擬時(shí)間長(zhǎng)的問(wèn)題依然比較突出。
堆用蒙特卡羅分析程序RMC(Reactor Monte Carlo Code)[6,7]是由清華大學(xué)工程物理系核能科學(xué)與工程管理研究所反應(yīng)堆工程計(jì)算分析實(shí)驗(yàn)室(REAL團(tuán)隊(duì))自主研發(fā)的,用于反應(yīng)堆堆芯計(jì)算分析的三維輸運(yùn)蒙特卡羅程序。RMC程序針對(duì)反應(yīng)堆計(jì)算分析中的基本需求,同時(shí)結(jié)合新概念反應(yīng)堆系統(tǒng)設(shè)計(jì)時(shí)幾何結(jié)構(gòu)靈活、中子能譜復(fù)雜及材料組分多樣、各向異性及泄露強(qiáng)(某些特定情況)等特點(diǎn)進(jìn)行研發(fā),是多物理多尺度耦合核能系統(tǒng)數(shù)值分析平臺(tái)的物理計(jì)算核心。
RMC程序有以下幾大特點(diǎn):
(1)MPI+OpenMP 2層并行。RMC程序?qū)崿F(xiàn)了MPI+OpenMP 2層并行計(jì)算模式,上層使用MPI實(shí)現(xiàn)對(duì)等模式下的并行計(jì)算,下層采用OpenMP實(shí)現(xiàn)多線程并行計(jì)算,用任務(wù)分解方法實(shí)現(xiàn)粒子并行。同時(shí),MPI也分成2級(jí)并行,第1級(jí)采用區(qū)域分解方法劃分幾何區(qū)域,以減少內(nèi)存占用量,第2級(jí)實(shí)現(xiàn)對(duì)粒子的并行計(jì)算。
(2) 大量采用vector作為基本數(shù)據(jù)類型。RMC程序使用C++作為開(kāi)發(fā)語(yǔ)言,程序中大量使用容器vector代替數(shù)組作為基本數(shù)據(jù)類型。vector可變?nèi)萘刻匦允沟镁幊虡O為方便,但容量變化的同時(shí)需要不停地從內(nèi)存中申請(qǐng)和釋放空間,尤其是在多線程的環(huán)境中,內(nèi)存申請(qǐng)和釋放會(huì)引起線程之間的資源競(jìng)爭(zhēng),從而導(dǎo)致程序性能下降。
(3) 文件I/O量大。RMC程序?qū)崿F(xiàn)了斷點(diǎn)續(xù)算功能,續(xù)算功能由主進(jìn)程控制,每隔一段時(shí)間主進(jìn)程負(fù)責(zé)收集各個(gè)進(jìn)程中粒子的相關(guān)信息,然后由主進(jìn)程輸出到相關(guān)文件中。當(dāng)計(jì)算規(guī)模比較大時(shí),續(xù)算文件的大小往往能達(dá)到幾十GB甚至上百GB,文件讀寫(xiě)均需要較長(zhǎng)的時(shí)間。
RMC程序在實(shí)際進(jìn)行反應(yīng)堆數(shù)值分析計(jì)算時(shí),對(duì)于千萬(wàn)網(wǎng)格的計(jì)算規(guī)模,在數(shù)千核的計(jì)算機(jī)上完成單個(gè)燃耗步的計(jì)算需要數(shù)小時(shí)之久,完成一次大規(guī)模的數(shù)值分析需要幾天甚至一周時(shí)間。在有限的計(jì)算資源上如何提高計(jì)算的效率是本文研究的主要目的。
根據(jù)RMC程序的特點(diǎn),本文先后開(kāi)展了以下工作。
RMC程序在內(nèi)存管理上有以下幾個(gè)特點(diǎn):
(1) 內(nèi)存分配量大,在進(jìn)行千萬(wàn)網(wǎng)格數(shù)值模擬時(shí),單個(gè)進(jìn)程所占內(nèi)存在70 GB以上。
(2) 多線程中使用vector容器,內(nèi)存申請(qǐng)釋放比較頻繁,線程之間內(nèi)存鎖操作開(kāi)銷較大,且容易產(chǎn)生內(nèi)存碎片。
在多線程計(jì)算環(huán)境中,要保證內(nèi)存數(shù)據(jù)的訪問(wèn)效率和穩(wěn)定性,首先要考慮多線程內(nèi)存管理的有效性,即內(nèi)存分配釋放的快速性、碎片率、利用率和擴(kuò)展性問(wèn)題。RMC程序使用vector容器作為基本數(shù)據(jù)類型,在多線程的計(jì)算過(guò)程中需要不斷地申請(qǐng)和釋放內(nèi)存資源,需要選擇高效的內(nèi)存管理算法來(lái)減少多線程環(huán)境中的鎖競(jìng)爭(zhēng)問(wèn)題。
多線程內(nèi)存管理方式主要采用多堆的組織方式,內(nèi)存管理算法用于處理堆、處理器和線程之間的對(duì)應(yīng)關(guān)系。目前PTMalloc是Linux的主流內(nèi)存管理算法,該算法為一種基于線程隨機(jī)挑選內(nèi)存堆的算法。該算法每次分配內(nèi)存首先隨機(jī)查找未被使用的內(nèi)存堆,然后在該內(nèi)存堆中分配內(nèi)存。 釋放內(nèi)存時(shí)需將分配的內(nèi)存堆收回該內(nèi)存堆。在多線程的環(huán)境中,該內(nèi)存管理算法可能存在多個(gè)線程共享一個(gè)內(nèi)存分配區(qū),使各個(gè)線程在申請(qǐng)釋放內(nèi)存時(shí)都需要加鎖、解鎖操作,且PTMalloc采用的是比較耗時(shí)的互斥鎖,這在一定程度上使得PTMalloc在多線程競(jìng)爭(zhēng)時(shí)的性能受到影響。
TCMalloc是Google開(kāi)發(fā)的面向多線程的內(nèi)存管理庫(kù)。與PTMalloc相比,TCMalloc具有更高的內(nèi)存分配效率和內(nèi)存利用率,提高了多線程并發(fā)環(huán)境中內(nèi)存管理的效率。TCMalloc的內(nèi)存結(jié)構(gòu)為三級(jí)緩存模型,分為線程局部緩存區(qū)(ThreadCache)、中央緩存區(qū)(CentralCache)和全局的頁(yè)面堆(PageHeap)。線程局部緩存區(qū)負(fù)責(zé)線程內(nèi)的內(nèi)存管理,當(dāng)局部緩存區(qū)能滿足線程分配內(nèi)存需求時(shí),線程內(nèi)無(wú)需鎖操作即可進(jìn)行內(nèi)存分配;當(dāng)局部緩存區(qū)無(wú)法滿足線程內(nèi)存分配需求時(shí),則采用鎖機(jī)制從中央緩存區(qū)和全局頁(yè)面堆分配內(nèi)存。頻繁地內(nèi)存操作時(shí),線程局部緩存區(qū)和中心緩存區(qū)之間可能存在周期性的抖動(dòng)現(xiàn)象。
本文基于TCMalloc的內(nèi)存管理算法,結(jié)合RMC程序的特點(diǎn),對(duì)TCMalloc大內(nèi)存的管理算法進(jìn)行了以下幾點(diǎn)改進(jìn)。
2.1.1 預(yù)先分配大量?jī)?nèi)存塊
在內(nèi)存結(jié)構(gòu)上,本文沿用TCMalloc的三級(jí)緩存結(jié)構(gòu),以保證內(nèi)存分配效率。由于RMC程序占用內(nèi)存量比較大,因此在初始化管理時(shí),本文根據(jù)RMC程序的內(nèi)存需求,提前設(shè)置好中央緩存區(qū)中的內(nèi)存數(shù)量、運(yùn)行時(shí)線程數(shù)以及每個(gè)線程局部?jī)?nèi)存區(qū)的內(nèi)存數(shù)量,預(yù)先分配大量?jī)?nèi)存塊到線程局部緩存區(qū)和中央緩存區(qū),以減少線程在分配內(nèi)存時(shí)向中央內(nèi)存區(qū)申請(qǐng)內(nèi)存和釋放內(nèi)存所帶來(lái)的鎖開(kāi)銷。
Figure 1 Loop iteration task queue of openMP schedule strategy圖1 OpenMP調(diào)度策略下循環(huán)迭代的任務(wù)隊(duì)列
2.1.2 一次申請(qǐng)多個(gè)大內(nèi)存塊
TCMalloc算法將大小在32 KB以內(nèi)的內(nèi)存塊稱為small內(nèi)存,將32~128 KB的內(nèi)存塊稱為large內(nèi)存,將超過(guò)128 KB的內(nèi)存塊稱為huge內(nèi)存。對(duì)于huge內(nèi)存TCMalloc直接使用mmap映射內(nèi)存的方法來(lái)進(jìn)行內(nèi)存申請(qǐng)操作。當(dāng)線程局部緩存區(qū)的內(nèi)存無(wú)法滿足huge內(nèi)存的申請(qǐng)操作時(shí),TCMalloc采用鎖機(jī)制一次性從全局頁(yè)面堆申請(qǐng)多個(gè)內(nèi)存堆用于huge內(nèi)存的分配操作,這樣避免了在大內(nèi)存分配時(shí)的多次鎖操作。預(yù)計(jì)一次性需要申請(qǐng)的內(nèi)存堆大小按式(1)計(jì)算:
(1)
其中,n為線程數(shù),α為內(nèi)存堆中的空間系數(shù),M為全局內(nèi)存堆的大小,Li為局部緩存區(qū)內(nèi)存塊總大小。
RMC程序中最主要的計(jì)算部分為粒子輸運(yùn)計(jì)算,即對(duì)所有粒子進(jìn)行追蹤(track)、采樣(sample)和碰撞(collide)計(jì)算。如在使用32進(jìn)程、每進(jìn)程創(chuàng)建10個(gè)線程計(jì)算共50萬(wàn)粒子的算例中,粒子輸運(yùn)計(jì)算部分占據(jù)了RMC程序約80%計(jì)算時(shí)間。
在RMC代碼中粒子輸運(yùn)部分體現(xiàn)為對(duì)粒子循環(huán)的for循環(huán)結(jié)構(gòu),由于各粒子模擬之間不存在數(shù)據(jù)依賴,RMC使用OpenMP對(duì)該循環(huán)體進(jìn)行了線程級(jí)并行,這同MPI并行一樣,是對(duì)粒子并行的粗粒度并行結(jié)構(gòu)。由于輸運(yùn)計(jì)算內(nèi)部有眾多判斷分支,每個(gè)粒子輸運(yùn)時(shí)間必然都不盡相同,且無(wú)法預(yù)測(cè),因此需要對(duì)該粗粒度的omp for循環(huán)體采用合適的并行調(diào)度策略,以盡量減少線程級(jí)并行計(jì)算時(shí)間。
OpenMP對(duì)循環(huán)體有3種調(diào)度策略:靜態(tài)調(diào)度(static)、動(dòng)態(tài)調(diào)度(dynamic)和指導(dǎo)調(diào)度(guided),并通過(guò)子句schedule(type,chunksize)來(lái)控制所采取的調(diào)度策略。循環(huán)體可以看成是各循環(huán)迭代塊組成的任務(wù)隊(duì)列,循環(huán)塊大小由chunksize設(shè)置。
圖1演示了當(dāng)循環(huán)結(jié)構(gòu)迭代900次、由3個(gè)線程并行、chunksize=100時(shí),3種調(diào)度策略的循環(huán)迭代塊劃分方式,以及迭代塊任務(wù)隊(duì)列分配方式。其中的方塊代表迭代塊,塊中數(shù)字表示迭代塊大小。
值得注意的是,guided調(diào)度策略中,隊(duì)列第k個(gè)迭代塊大小由式(2) 確定,依次減小,直至減小到chunksize。
(2)
其中,LCk為第k個(gè)迭代塊之后剩余的迭代次數(shù),NP為線程數(shù),p為比例因子,編譯器中通常設(shè)置為1。
對(duì)于無(wú)法預(yù)測(cè)每個(gè)粒子輸運(yùn)時(shí)間的隨機(jī)循環(huán)結(jié)構(gòu),應(yīng)該采用dynamic或guided動(dòng)態(tài)調(diào)度策略,并讓迭代塊chunksize盡量小,這將使得各線程的計(jì)算負(fù)載更加平衡。不過(guò)static調(diào)度迭代塊分配方式固定,而dynamic和guided調(diào)度是由運(yùn)行時(shí)系統(tǒng)動(dòng)態(tài)決定的,相較static方法,dynamic、guided方法和較小的chunksize會(huì)增加系統(tǒng)調(diào)度開(kāi)銷。但是,考慮到大規(guī)模問(wèn)題中粒子抽樣、追蹤花費(fèi)的計(jì)算時(shí)間較長(zhǎng),可以忽略增加的調(diào)度開(kāi)銷??偠灾?,針對(duì)蒙特卡羅類粒子輸運(yùn)程序,采用chunksize小的動(dòng)態(tài)調(diào)度策略,理論上能得到更短的整體計(jì)算時(shí)間。
許多計(jì)算機(jī)系統(tǒng)對(duì)基本類型的數(shù)據(jù)在內(nèi)存中存放的位置有限制,它們會(huì)要求這些數(shù)據(jù)的首地址是某個(gè)數(shù)的倍數(shù),通常是4或者8的倍數(shù),這就是所謂的內(nèi)存對(duì)齊。在多數(shù)情況下,經(jīng)過(guò)內(nèi)存對(duì)齊后,CPU的內(nèi)存訪問(wèn)速度會(huì)大大提升,也更容易對(duì)數(shù)據(jù)做向量化之類的數(shù)據(jù)并行調(diào)優(yōu)。因此,如果要提升應(yīng)用程序的性能,所有的程序數(shù)據(jù)都應(yīng)盡可能地對(duì)齊?,F(xiàn)在大多數(shù)的編譯器都已經(jīng)實(shí)現(xiàn)了自動(dòng)對(duì)齊策略,但手動(dòng)的數(shù)據(jù)對(duì)齊仍然是常用的應(yīng)用程序性能優(yōu)化手段。
RMC程序以vector作為基本數(shù)據(jù)類型,vector是C++容器中最常用的基本數(shù)據(jù)類型之一。C++中的vector數(shù)據(jù)類型原型聲明如下所示:
template 〈typename _Tp,typename _alloc=std::allocator〈_Tp〉〉
class vector::protected _Vector_Vase〈_Tp,_Alloc〉;
vector底層默認(rèn)調(diào)用系統(tǒng)的std:allocator作為內(nèi)存分配器,而std::allocator分配器通過(guò)Linux系統(tǒng)默認(rèn)內(nèi)存分配函數(shù)malloc實(shí)現(xiàn),分配后的內(nèi)存首地址是否對(duì)齊取決于編譯器。使用自定義的對(duì)齊內(nèi)存分配器(AlignedAllocator)替換vector默認(rèn)分配器std::allocator,便可強(qiáng)制實(shí)現(xiàn)vector內(nèi)存首地址對(duì)齊。不同于std::allocator利用malloc函數(shù),AlignedAllocator利用C語(yǔ)言底層內(nèi)存分配函數(shù)posix_memalign實(shí)現(xiàn)對(duì)齊內(nèi)存分配。
其中AlignedAllocator部分定義如下所示:
template 〈typenameT,AlignmentAlign〉
classAlignedAllocator
{
public:
typedefT*pointer;
typedef constT*const_poster;
pointer allocate(size_tn,typenameAlignedAllocator〈void,Align〉::const_pointer=0)
{
const size_talignment=static_cast〈size_t〉(Align);
void *ptr=allocate_aligned_memory(alignment,n*sizeof(T));
if(ptr== nullptr) {
throw std::bad_alloc();
}
returnreinterpret_cast〈pointer〉(ptr);
}
void * allocate_aligned_memory(size_talign,size_tsize)
{
assert(align>=sizeof(void*));
if(size== 0) {
returnnullptr;
}
void*ptr=nullptr;
intrc=posix_memalign(&ptr,align,size);
if(rc!= 0) {
returnnullptr;
}
returnptr;
}
}
HDF5(Hierarchical Data Format Version 5)是近年來(lái)流行的一種針對(duì)科學(xué)數(shù)據(jù)進(jìn)行高效存儲(chǔ)和管理的新型數(shù)據(jù)存儲(chǔ)方案,RMC程序同樣采用HDF5作為其中間續(xù)算文件數(shù)據(jù)存儲(chǔ)方案。
HDF5使用類似UNIX文件系統(tǒng)的層次結(jié)構(gòu)來(lái)描述所存儲(chǔ)的數(shù)據(jù)集。如用/foo/zoo來(lái)表示一個(gè)名為foo的group之下名為zoo的dataset。dataset是HDF5數(shù)據(jù)的基本類型,主要包括數(shù)據(jù)本身和描述數(shù)據(jù)用的元數(shù)據(jù)。
如圖2a所示,HDF5的串行I/O方式通常是按照f(shuō)ile→group→dataset→dataspace→data流程依次創(chuàng)建各對(duì)象,最終由單個(gè)進(jìn)程將數(shù)據(jù)讀/寫(xiě)進(jìn)dataset中。HDF5也能夠進(jìn)行并行I/O,它通過(guò)底層調(diào)用MPI-2并行I/O接口來(lái)實(shí)現(xiàn)并行I/O,可以由多個(gè)進(jìn)程同時(shí)讀寫(xiě)文件中同一個(gè)dataset的各部分。如圖2b所示,編寫(xiě)并行代碼需要在串行流程上增加一些操作,首先需要給file和dataset賦予mpio屬性,將dataspace劃分成歸屬各子進(jìn)程的slabspace,各子進(jìn)程在slabspace基礎(chǔ)上,同時(shí)讀寫(xiě)劃分成子數(shù)據(jù)的subdata。這樣便完成了并行I/O操作。
Figure 2 Programming process of HDF5圖2 HDF5編程流程
RMC的續(xù)算文件通常需要保存幾何、材料、粒子、計(jì)數(shù)器和燃耗等數(shù)據(jù),這些數(shù)據(jù)主要與網(wǎng)格規(guī)模和粒子規(guī)模有關(guān)。在大規(guī)模算例中,續(xù)算文件通常達(dá)到幾十、上百GB。例如在320萬(wàn)粒子的千萬(wàn)級(jí)網(wǎng)格算例中,文件大小高達(dá)148 GB。由于RMC對(duì)粒子進(jìn)行了數(shù)據(jù)分解、對(duì)網(wǎng)格進(jìn)行了區(qū)域分解,從而續(xù)算文件所需數(shù)據(jù)分散在各進(jìn)程中。而RMC程序通常采用HDF5串行I/O方式訪問(wèn)續(xù)算文件,即主進(jìn)程在進(jìn)行數(shù)據(jù)讀寫(xiě)之前,必須先從其他進(jìn)程中收集完整信息,再將信息數(shù)據(jù)寫(xiě)入/讀出文件。而采用HDF5并行I/O方式,主進(jìn)程無(wú)需收集數(shù)據(jù),各進(jìn)程便可直接將子數(shù)據(jù)寫(xiě)入/讀出文件,從而節(jié)省MPI通信時(shí)間。尤其對(duì)于RMC具有幾十、上百GB續(xù)算文件數(shù)據(jù)的大規(guī)模算例,并行I/O方式能夠節(jié)省的通信開(kāi)銷非常可觀。
此外,相比串行I/O,并行I/O具有更高的讀寫(xiě)速度,不過(guò)并行I/O性能受到計(jì)算機(jī)系統(tǒng)存儲(chǔ)硬件配置的限制。一般來(lái)說(shuō),數(shù)據(jù)存儲(chǔ)服務(wù)器結(jié)點(diǎn)越多,I/O并行度越高,并行I/O的加速優(yōu)勢(shì)越能體現(xiàn)出來(lái)。
本節(jié)對(duì)以上優(yōu)化方法開(kāi)展單項(xiàng)性能測(cè)試,測(cè)試在高性能計(jì)算系統(tǒng)上進(jìn)行,該系統(tǒng)每個(gè)計(jì)算結(jié)點(diǎn)包括2個(gè)Intel Xeon E5-2660 處理器(主頻2.6 GHz、10個(gè)核,每核包括32 KB L1數(shù)據(jù)Cache和32 KB L1指令Cache,256 KB L2 Cache,10核共享25 MB L3 Cache)和256 MB內(nèi)存,結(jié)點(diǎn)間使用國(guó)防科技大學(xué)自主研發(fā)的Express高速互連網(wǎng)絡(luò),通信帶寬25 Gbps。系統(tǒng)采用Lustre并行文件系統(tǒng),共有16個(gè)存儲(chǔ)結(jié)點(diǎn)。編譯環(huán)境使用基于gcc 4.8.5的Intel C/C++Compiler 16.0.3,并行環(huán)境使用針對(duì)Express-2高速網(wǎng)絡(luò)優(yōu)化的mpich3。
將優(yōu)化后的TCMalloc庫(kù)編譯成靜態(tài)庫(kù),在RMC程序編譯階段鏈接靜態(tài)庫(kù),程序運(yùn)行期間會(huì)自動(dòng)調(diào)用優(yōu)化后的內(nèi)存分配方法。對(duì)RMC程序進(jìn)行實(shí)際性能測(cè)試,測(cè)試規(guī)模盡可能重現(xiàn)工業(yè)算例所需的運(yùn)行模式,固定進(jìn)程數(shù)為32,每個(gè)進(jìn)程創(chuàng)建10個(gè)線程,改變粒子數(shù),分別測(cè)試不同粒子數(shù)情況下程序的執(zhí)行時(shí)間,結(jié)果如圖3所示。
Figure 3 Test results using optimized dynamic memory allocation method圖3 動(dòng)態(tài)內(nèi)存分配優(yōu)化測(cè)試結(jié)果
在1 200萬(wàn)網(wǎng)格數(shù)量的實(shí)驗(yàn)測(cè)試中,分別測(cè)試了200萬(wàn)粒子數(shù)、150萬(wàn)粒子數(shù)和100萬(wàn)粒子數(shù)情況下程序總體的運(yùn)行時(shí)間。在粒子數(shù)為200萬(wàn)時(shí),未使用內(nèi)存優(yōu)化管理方法、使用TCMalloc和使用優(yōu)化的TCMalloc 3種情況下程序的運(yùn)行時(shí)間分別為783 s, 683.2 s和671.4 s,使用本文優(yōu)化的內(nèi)存管理方法,較使用系統(tǒng)默認(rèn)的內(nèi)存管理方法,程序總體性能提升了16.25%,較使用TCMalloc提升了1.7%。
通過(guò)在omp制導(dǎo)指令中調(diào)整schedule子句,便可指定OpenMP調(diào)度策略,如使用如下制導(dǎo)指令便可將調(diào)度策略修改為迭代塊大小為100的動(dòng)態(tài)調(diào)度策略:
#pragma omp parallel for schedule (dyna- mic,100)
RMC程序中對(duì)粒子追蹤和隨機(jī)抽樣過(guò)程的循環(huán)結(jié)構(gòu)采用的原始調(diào)度策略是chunksize=nIter/nThread的guided調(diào)度,這種調(diào)度結(jié)果其實(shí)和缺省static調(diào)度一樣,沒(méi)有達(dá)到負(fù)載平衡效果。
為了改善這種不合理的調(diào)度策略,本文采用了(guided,1),(guided,100),(dynamic,1),(dyna- mic,100)4種較小迭代塊的動(dòng)態(tài)調(diào)度策略,并同原始調(diào)度(guided,nIter/nThread)和缺省static調(diào)度進(jìn)行了對(duì)比。表1展示了對(duì)于6百萬(wàn)級(jí)網(wǎng)格算例,使用32個(gè)進(jìn)程,每個(gè)進(jìn)程創(chuàng)建10個(gè)線程,計(jì)算200萬(wàn)個(gè)粒子,迭代200次時(shí),采用6種不同OpenMP調(diào)度策略的RMC計(jì)算時(shí)間。原始調(diào)度策略耗時(shí)最長(zhǎng),約252.4 s,采用(guided,1),(guided,100),(dynamic,1)的計(jì)算時(shí)間最短,約為234.1~238.5 s,整體性能提升約5.82%~7.81%。這表明,對(duì)于由粒子模擬存在眾多條件語(yǔ)句分支的蒙特卡羅程序,采用chunksize小的OpenMP動(dòng)態(tài)調(diào)度策略,能夠在一定程度上提升性能。
Table 1 Computation time of particle transportation module under different scheduling strategies
在RMC程序中,把自定義的內(nèi)存對(duì)齊分配器(AlignAllocator)應(yīng)用到vector的數(shù)據(jù)類型定義中的方式如下所示:
vector〈int,AlignedAllocator〈int,64〉〉 Array;
本節(jié)對(duì)于1 200萬(wàn)網(wǎng)格算例,使用32個(gè)進(jìn)程,每進(jìn)程創(chuàng)建10個(gè)線程,分別測(cè)試了100萬(wàn)粒子數(shù)和50萬(wàn)粒子數(shù)情況下程序的運(yùn)行時(shí)間,表2為測(cè)試結(jié)果。
Table 2 Test results using vector memory alignment optimization表2 vector內(nèi)存對(duì)齊優(yōu)化測(cè)試結(jié)果
從表2中可以看出,使用內(nèi)存對(duì)齊優(yōu)化方法后,程序總體提升僅有1.8%,說(shuō)明程序在編譯階段就已盡可能地實(shí)現(xiàn)了內(nèi)存對(duì)齊,但手動(dòng)的內(nèi)存對(duì)齊仍然能使程序性能提高。
在RMC程序中,HDF5原本使用串行I/O技術(shù),本文采用并行I/O技術(shù)后,程序性能得到極大提升。HDF5并行I/O實(shí)現(xiàn)方法主要在于,在串行I/O代碼的基礎(chǔ)上,添加mpio屬性,2.4節(jié)中闡述了具體方式。本文進(jìn)行了2組測(cè)試:第1組使用16個(gè)進(jìn)程,測(cè)試了400萬(wàn)粒子數(shù)、600萬(wàn)網(wǎng)格單元規(guī)模、72 GB續(xù)算文件的算例,測(cè)試結(jié)果如表3所示;第2組使用了64個(gè)進(jìn)程,測(cè)試了320萬(wàn)粒子數(shù)、1 200萬(wàn)網(wǎng)格單元規(guī)模、146 GB續(xù)算文件的算例,測(cè)試結(jié)果如表4所示。
Table 3 HDF5 I/O test under 6 million grids
Table 4 HDF5 I/O test under 12 million grids
第1組測(cè)試中,讀寫(xiě)時(shí)間分別縮短了90.12%和85.72%;第2組測(cè)試中,讀寫(xiě)時(shí)間分別縮短了96.30%和95.18%??梢?jiàn)HDF5的并行I/O技術(shù)極為有效地提升了文件讀寫(xiě)性能,而且網(wǎng)格規(guī)模越大,性能提升越明顯。
將以上4種優(yōu)化方法全部應(yīng)用到RMC程序中,對(duì)RMC程序進(jìn)行優(yōu)化后的整體性能測(cè)試。測(cè)試中同樣固定進(jìn)程數(shù)為32,每個(gè)進(jìn)程創(chuàng)建10個(gè)線程,分別計(jì)算在1 200萬(wàn)網(wǎng)格情況下,200萬(wàn)粒子規(guī)模和100萬(wàn)粒子規(guī)模的程序總運(yùn)行時(shí)間,測(cè)試結(jié)果如表5所示。從表5中可以看出,2種粒子規(guī)模下,程序的總體性能提升均達(dá)到了26.45%以上。
Table 5 Test results of overall performance
本文針對(duì)堆用蒙特卡羅分析程序(RMC),先后開(kāi)展了一系列的程序性能優(yōu)化研究,采取了基于TCMalloc動(dòng)態(tài)內(nèi)存分配優(yōu)化、OpenMP線程并行調(diào)度策略優(yōu)化、vector內(nèi)存對(duì)齊優(yōu)化和基于HDF5的并行I/O優(yōu)化等優(yōu)化方法,并對(duì)優(yōu)化后的程序進(jìn)行了實(shí)際性能測(cè)試。測(cè)試結(jié)果表明,每種優(yōu)化方法都使程序性能得到一定程度的提升,其中動(dòng)態(tài)內(nèi)存分配優(yōu)化方法使程序性能整體提升了16.25%,表明在RMC程序中,頻繁的內(nèi)存分配是造成程序性能較低的主要原因之一,新的內(nèi)存管理方法有效提高了多線程環(huán)境中內(nèi)存管理的效率?;贖DF5的并行I/O優(yōu)化極大地縮減了程序續(xù)算文件讀寫(xiě)時(shí)間。相較串行I/O方式,進(jìn)程數(shù)越多,并行I/O時(shí)間越短。因此,使用HDF5的并行I/O非常有效。綜上所述,采用一系列的優(yōu)化方法優(yōu)化后,程序整體性能提高26.45%以上。