3月23日起,智東西聯(lián)合NVIDIA推出「實戰(zhàn)營」第一季,共計四期。第四期于4月20日晚8點在智東西「高性能計算」系列社群開講,由清華大學計算機系副教授都志輝、NVIDIA高級系統(tǒng)架構(gòu)師易成二位講師先后主講,主題分別為《GPU加速的實時空間天氣預(yù)報》和《NVIDIA GPU加速高性能計算》。 大型的科學應(yīng)用程序往往由于其代碼量和通信量巨大,對計算力和數(shù)據(jù)傳輸性能要求非常高,如何利用高性能計算技術(shù),設(shè)計開發(fā)高效的并行計算模型與算法,充分利用不斷提高的硬件系統(tǒng)的計算能力來解決科學與工程計算問題具有重大意義,清華大學計算機系副教授都志輝團隊采用PPMLR-MHD方法并利用GPU加速運算處理有效解決了實時空間天氣預(yù)報問題。 本文為都志輝教授的主講實錄,共計7721字,預(yù)計14分鐘讀完。在瀏覽主講正文之前,可以思考以下三個問題: -如何有效的將大量代碼從CPU移植到GPU上,并保證運算結(jié)果的一致性? 智東西「實戰(zhàn)營」第一季第一期由圖瑪深維首席科學家陳韻強和NVIDIA高級系統(tǒng)架構(gòu)師付慶平,分別就《深度學習如何改變醫(yī)療影像分析》、《DGX超算平臺-驅(qū)動人工智能革命》兩個主題在智東西旗下「智能醫(yī)療」社群進行了系統(tǒng)講解。第二期由NVIDIA深度學習學院認證講師侯宇濤主講,主題為《手把手教你使用開源軟件DIGITS實現(xiàn)目標檢測》。第三期由西安交通大學人工智能與機器人研究所博士陶小語、NVIDIA高級系統(tǒng)架構(gòu)師易成二位講分別就《智能監(jiān)控場景下的大規(guī)模并行化視頻分析方法》和《NVIDIA DGX-2 驅(qū)動智能監(jiān)控革命》兩個主題在智東西旗下「智能安防」社群進行了系統(tǒng)講解。 主講環(huán)節(jié) 都志輝:大家好,我叫都志輝。很高興能夠在這里跟大家一塊交流我們在GPU加速空間天氣預(yù)報方面的一些工作,我今天分享的主題是《GPU加速的實時空間天氣預(yù)報》。 今天的內(nèi)容分為三個方面:
空間天氣預(yù)的概念和意義 首先介紹一下空間天氣預(yù)報的概念,空間天氣預(yù)報可以類比地面的天氣預(yù)報。比如刮風下雨對我們有什么影響,只不過空間天氣預(yù)報是在更遠的地方,主要研究太陽與地球的相互作用對地球外部環(huán)境的影響。 在地球上,主要是刮風、下雨對我們有影響;而在空間是太陽風,即太陽發(fā)出很強勁的帶電粒子,會對地球造成非常大的影響。 對地球的影響有很多方面,對通信、雷達信號、發(fā)射衛(wèi)星和電視轉(zhuǎn)播等都會造成影響。根本原因是這些帶電粒子干擾了地球的外層空間環(huán)境,從而對地球上的各種通信網(wǎng)絡(luò)、電力網(wǎng),甚至是臭氧層都造成影響。 如果沒有地球的磁場把太陽給推開,那么地球上就不可能有人類,太陽粒子會直接把人類殺死。進入地區(qū)的太陽例子就像一個很強的X射線一樣進入了人體,相當于人體接觸了大量的輻射,會對人體的健康造成非常有害的影響。 空間天氣預(yù)報的計算的難點以及挑戰(zhàn) 如果把問題抽象起來,就是一個磁場和一個帶電的離子流之間的相互作用,在數(shù)學上可以用磁流體動力學方程來進行描述。 這個方程建立起來之后是非常復(fù)雜的,有很多不同的解法。其中我們的合作方(空間科學中心)提出了一種叫PPMLR的方法來解磁流體動力學方程。 他們用了十幾年的時間來發(fā)展這個方法,而且也已經(jīng)形成了一個比較穩(wěn)定的程序。這個方法的好處在于可以得到比較高精度的仿真計算結(jié)果,但計算量、通信量也非常大。 因此目前空間的空間天氣預(yù)報面臨的主要挑戰(zhàn)跟很多大規(guī)??茖W工程計算問題一樣,都是如何才能算得更快,如何才能夠完成實時計算的問題。對于實時預(yù)報問題,空間科學中心告訴我們理想的情況是在40到50分鐘之內(nèi)把全部的結(jié)果仿真出來。用原來的程序完成這個計算需要2-3個小時,所以我們面對的問題是如何把2-3小時的計算時間壓縮到1小時之內(nèi)。 他們的方法非常好,所有的科學工程計算問題都首先得有數(shù)值計算方法,然后才是實現(xiàn)。這里就不進行仔細地介紹了,當然一個好的數(shù)值計算方法也是非常重要的,這里我們重點講一下如何在GPU上進行程序代碼的實現(xiàn)和優(yōu)化。 這是SCALE CHALLENGE AWARD證書,是我們把空間科學中心的大型應(yīng)用程序放在GPU集群上進行性能優(yōu)化,實現(xiàn)了實時的計算,我們把這個成果參加了一個比賽,在IEEE的可擴展計算委員會經(jīng)過答辯評審后授予了這個證書。 如果大家也做了相關(guān)的事情,比如做了科學工程計算,解決了一個很實際的問題,效果也非常好,也可以試著去參加一下這個競賽,在CCGRID的會議投稿期間,可以把你的工作寫成類似于論文的東西投過去,同時需要指明你是要參加這個競賽。他們首先要進行評審,評審之后還要去進行答辯,答辯完了之后再評價,最后挑選出他們認為做得比較好的項目,并授予相應(yīng)的獎項。 這是空間科學中心已有程序的大概計算流程。這個程序分為兩部分,一部分是計算太陽風和地球磁層之間的相互作用;另一部分是計算地球的電離層。將這兩部分相互計算的結(jié)果最后綜合到一起形成總的仿真結(jié)果。 計算量比較大的是太陽風和磁層的相互作用這一部分,而右邊相對來說計算量很小,所以在他們的程序里面就出現(xiàn)了跟一般的并行不太一樣的地方,就是用一個MPI進程來專門計算電離層這部分的任務(wù),而其他所有的進程來計算太陽風和磁層相互作用這一部分。 我們在GPU上加速以及原來在MPI并行的也主要是左邊這一部分。大家可以看到有sweepx、y、z三個過程,同時最主要的計算量也是在這三個過程里面,而我們做的事情就是對這三個過程分別在GPU上又進一步進行加速。 在具體介紹我們怎么優(yōu)化這個程序之前,先大概講一下我們在優(yōu)化這個程序時獲得的一些體會。在優(yōu)化這個程序的同時,我們還做了一個大型雙黑洞仿真的MPI并行程序,將其放在GPU集群上進行了性能優(yōu)化,這些大型應(yīng)用程序的代碼比較多,一般有幾十萬行,而這個程序只有幾萬行。 通過把這個大型的科學工程應(yīng)用程序進行性能優(yōu)化,在GPU上進行移植等,完成這件事情得到的體會是它和一般的小程序Benchmark有非常大的不同: 1,源程序的代碼量不一樣,一般大型程序都在幾萬行以上,而一般Benchmark只有幾百行,最多是一兩千行。對于Benchmark這樣的小程序來說,由于代碼量很少,因此在優(yōu)化的過程中,你就可以非常仔細地對每一條語句進行研究。而對于十幾萬行代碼的大型程序,這樣操作的工作量就非常大。這也是大型程序性能優(yōu)化在GPU上進行移植面臨的一個非常大的問題; 2,控制邏輯和數(shù)據(jù)結(jié)構(gòu)不同,由于大型應(yīng)用程序代碼量大,必然導致其控制邏輯和數(shù)據(jù)結(jié)構(gòu)與benchmark非常不同,對于大型程序來說不是那么簡單就可以搞得明白,而Benchmark可以很容易把整個代碼做的事情搞得一清二楚。 我們在將空間天氣預(yù)報程序在GPU集群上進行移植以及將更大的雙黑洞仿真程序在GPU上的移植時,發(fā)現(xiàn)一個相同的規(guī)律,就是代碼移植本身并不是太難,對于一個對CUDA編程非常熟練的人來說,只要用一個月的時間就可以把上萬行甚至幾十萬行的代碼移植到GPU上,而難的是將代碼移植到GPU上之后,一開始運行就會出現(xiàn)這樣那樣的意外與錯誤,就算你把這些問題都解決了之后,也會發(fā)現(xiàn)一個非常難解決的問題,就是在GPU和CPU上運行的結(jié)果不一樣。 我們的學生,他們之前用GPU用得很熟,算是CUDA編程高手,所以一開始對這么大的程序代碼是非常有信心的,覺得移植到GPU上沒有問題,事實上一開始的代碼移植也的確是這樣,但問題是將代碼移植到GPU上之后,不能保證這些代碼是正確的。 這兩個大型程序的移植都出現(xiàn)了相同的情況,我們調(diào)試錯誤所花的時間要遠遠超過代碼移植所花的時間,甚至有些學生調(diào)試到最后都已經(jīng)完全崩潰了,感到非常絕望,并且不知道該從哪下手了,雖然他們一開始都覺得自己是GPU編程的高手,但是對于大型程序中出現(xiàn)的各種莫名其妙的錯誤都感到束手無策。 利用GPU實現(xiàn)加速空間天氣預(yù)報采取的方法 下面我來講一下我們是如何在GPU集群上移植代碼的。 首先這個代碼不是串行代碼,前面已經(jīng)介紹過,它是空間科學中心花了多年的時間,用MPI在CPU集群上可以穩(wěn)定并行運行的并行代碼。 這個代碼有些特殊的地方,就是實際應(yīng)用程序和Benchmark不同,用Benchmark實現(xiàn)的某些方法和手段,在實際應(yīng)用程序中,經(jīng)常發(fā)現(xiàn)是不能解決問題的,原因就是大型應(yīng)用程序總有很多特殊的情況,而這些特殊的情況會讓那些從Benchmark里面總結(jié)的經(jīng)驗變得不可行。 比如上圖這個程序,它對網(wǎng)格的劃分比較特殊,它不是均勻的劃分,大概意思是,越靠近地球的地方相對來說越稠密,而離地球越遠的地方相對稀疏一些,這也是所有的網(wǎng)格劃分中基本的指導思想。 這個算法的實現(xiàn)還有一個特點,在對空間網(wǎng)格劃分以后,對各個坐標軸的約束也不一樣。它需要專門拿出一個進程來仿真電離層的計算,而其他的進程用來仿真太陽風與地球的相互作用,為了讓地球都處在特定的位置,就要求它坐標軸的y和z軸的進程所劃分的塊數(shù)必須是奇數(shù)。 如上圖所示,以地球為中心進行156*150*150的空間網(wǎng)格劃分,在這個基礎(chǔ)上可以并行的進程數(shù)并不能隨便選,而只能是某些特定的值,比如4,28,37等這些值。 這里我跟大家介紹的這些特別細節(jié)的內(nèi)容,我覺得大家可能沒必要去掌握。我想從中說明一件事情,就是對大型應(yīng)用程序來說,往往會有很多特殊的要求,而這些特殊的要求可能會對你的性能優(yōu)化或者以前在別的地方積累起來的一些經(jīng)驗造成很大的障礙,在這樣的場景下已有的經(jīng)驗可能并不適用或者說效果并不好。 同時這也說明一件事情,我們經(jīng)常說GPU的性能非常高,有成百甚至上千倍的加速。這個加速往往是在Benchmark理想情況下得到的,而在真實的大型應(yīng)用程序上,如果想得到非常高的性能加速,一般來說是特別難的,難的原因就是大型應(yīng)用程序本身不管是CPU還是GPU并行都不是特別理想,與理想情況不太吻合,因此給并行和優(yōu)化加速制造了非常多的困難和障礙。 我們把程序在GPU集群上進行移植和優(yōu)化的過程分為兩步: 第一步,是一個摸索的過程,在MPI并行程序的基礎(chǔ)上,對代碼進行處理,其中代碼分兩部分,一部分是計算量比較少的,用一個MPI進程在CPU上計算就可以完成。另一部分是計算量比較大的,就是太陽風與地球磁層相互作用的那一部分,需要將它全部移植到GPU上進行計算。 我們之所以把這么多的代碼全都移植到GPU上,而不是將一部分放在CPU上一部分核心的sweepX、Y、Z三個過程放在GPU上執(zhí)行,主要是因為如果你把一部分代碼放在CPU上,一部分放在GPU上,計算是可以大大的加速的,但是部分代碼放在CPU上會導致CPU和GPU之間存在大量的數(shù)據(jù)交換,如此一來,之前得到的加速計算的效果都會被這些數(shù)據(jù)交換的開銷所淹沒。 正是因為這個原因,所以我們才不得不把這么多的代碼放在GPU上進行移植,盡管有些代碼并不是計算密集部分的代碼或者也不是一些瓶頸的代碼,這個問題在很多GPU性能優(yōu)化上都會存在的,其根本原因是因為目前在GPU上執(zhí)行代碼,得先把它需要的數(shù)據(jù)放上去,放數(shù)據(jù)是需要很大的開銷的。因此為了避免反反復(fù)復(fù)地在CPU和GPU之間來回拷貝數(shù)據(jù),我們才把更多的代碼放到GPU上來執(zhí)行。 對于這個過程,像剛才說的,一開始看了這個代碼之后很有信心,認為移植上去不會有問題。然后不到一個月,就把原來在MPI在 CPU上并行的代碼全都移植到GPU上去了,但是到后來一測試,就發(fā)現(xiàn)會出現(xiàn)各種各樣的錯誤。開始我們還是比較有信心的,也很快地把很多錯誤都搞定了。但是后面不斷測試,就不斷會有新的錯誤出來,一直到最后,就發(fā)現(xiàn)有些錯誤根本就不知道是什么原因?qū)е碌摹?/p> 在這種情況下,我們不得不反思,移植上去的代碼可能是一個失敗的嘗試,換句話說我們根本沒有辦法保證這些代碼是正確的,也不可能通過調(diào)試把每一條的錯誤都找出來。在經(jīng)過思考以后,我們就提出了第二版移植方案,其中的基本想法就是分步走,不要想著一步就把原來的代碼直接移植到GPU上,這也是從CPU程序向GPU移植經(jīng)常會犯的一個錯誤,一般將代碼直接直截了當?shù)匾浦驳紾PU上往往都是有問題的。 因為CPU程序的執(zhí)行流程和計算方式與GPU有很大的不同,包括它們的數(shù)據(jù)結(jié)構(gòu)和流程都不一樣。如果將代碼直接移植到GPU上,它的性能是會受影響的,或者你要改動非常大才能滿足性能要求,所以這種Straight forward移植是有很大問題的。而我們認識到這個錯誤以后,我們的方案是不把代碼直接移植到GPU上,而是先分析原來的代碼,如果要準備移植到GPU上,什么樣的一個代碼結(jié)構(gòu)與數(shù)據(jù)結(jié)構(gòu)更有利于移植。 第二步,先把數(shù)據(jù)結(jié)構(gòu)和代碼結(jié)構(gòu)分別進行重組,但都還是用C或者用Fortran在CPU上進行編程,比如循環(huán)結(jié)構(gòu)和數(shù)據(jù)結(jié)構(gòu),CPU上的哪些數(shù)據(jù)結(jié)構(gòu)跟GPU的特性有關(guān)聯(lián),相關(guān)聯(lián)的結(jié)構(gòu)重組后,將代碼移植到GPU上后才不會造成混亂的局面。 把這些代碼捋順以后,看上去這些代碼看上去還是CPU上的代碼,但是它是按照GPU上的邏輯,為了能在GPU上執(zhí)行的邏輯和數(shù)據(jù)組織方式去重寫的。通過調(diào)試,確保這部分代碼是正確的。 在這樣的基礎(chǔ)上,我們才將代碼往GPU上進行移植。剛才也說到,代碼量太大,為了避免CPU和GPU之間頻繁的數(shù)據(jù)交換,需要將很多代碼都轉(zhuǎn)移到GPU上,很明顯這工作量太大。但是如果特別影響性能的那部分核心代碼又不是特別多,我們可以采取引入Open ACC的方法。 引入Open ACC的目的,是對于那些我們不得不移植到GPU上的、但即使這部分代碼經(jīng)過優(yōu)化以后也不可能對性能有非常大的提升的不太核心的代碼來說,我們可以通過Open ACC加一些簡單的注釋語句,將它變成能在GPU上執(zhí)行的代碼。而對于那小部分對性能影響非常大的代碼,可以專門用CUDA來進行重寫。 通過這種方式,就達到了以下的效果: 第一,通過使用Open ACC的方式,使得工作量大大降低,而且不容易出錯,降低了出錯的概率;
以上說的就是我們在整個移植過程中總結(jié)出來的一些經(jīng)驗,就是通過這種方式,既避免了CPU上大量的代碼移植到GPU上重寫的繁瑣過程,又能保證對性能沒有造成很大的影響。 因此大型應(yīng)用程序在GPU或者GPU集群上的移植和性能優(yōu)化,它的第一個難點并不是性能的提升,而是如何有效地控制代碼移植的復(fù)雜性。 我們采取的辦法就是,首先經(jīng)過代碼的重組,構(gòu)造一個中間版本,而這個中間版本跟原來的程序一樣,都是用C或者用Fortran語言來寫的,但是它比原來的程序更容易移植到GPU上去,因為它是按照GPU的邏輯來組織、重寫的,但它又是CPU的代碼,這樣的中間代碼就容易符合GPU上的邏輯;第二步就是采用Open ACC方法,避免了手工重寫大量與性能無關(guān)的代碼。 還有一個難點,對大型程序來說,要找到真正影響性能的地方往往也是很難的。有時候可能自己感覺用shared memory能提高性能,但是用了以后效果可能不一定真的好。我們在做用GPU來加速引力波數(shù)據(jù)處理的時候,就發(fā)現(xiàn)如果用shared memory來進行優(yōu)化,還不如用atomic操作最終得到的效果要好。 NVIDIA的編程指導中講了很多通用的GPU性能優(yōu)化的方法,比如提高occupancy,使用shared memory等這些手段,但是在真實的應(yīng)用場景下,這些優(yōu)化手段并不是用了就一定能夠得到好的結(jié)果,有的時候用了可能還會帶來另外的負面影響,從而導致整體性能沒有提高,反而可能有下降的情況。 這就是我想說的另外一個難點,如何才能找到真正可以影響性能的優(yōu)化點,這個是非常難的,因為大型程序它存在很多會影響性能優(yōu)化的因素,可能計算性能提高了,但是通信的開銷大了,性能也不一定好,或者是并行計算提高了,但數(shù)據(jù)訪問的次數(shù)提高了,開銷大了,也有可能會影響性能。 ![]() 下面具體講講我們對大型應(yīng)用程序,再深入到具體的GPU代碼編程大概用哪些優(yōu)化手段和方法。 第一個比較簡單,對每一個MPI進程都調(diào)用一個CUDA核心來并行實現(xiàn),就是把原來CPU上一個進程的執(zhí)行代碼變成一個CUDA的并行執(zhí)行(代碼)。 另外一個方面是如何提高訪存的效率。由于我們在具體應(yīng)用中,在解方程的過程中,幾乎沒有數(shù)據(jù)的重用,因此我們沒必要再用shared memory,并且在這時候用,并不能帶來性能的提升。而能夠提高性能的,就是想辦法重新調(diào)整數(shù)據(jù)的結(jié)構(gòu),讓數(shù)據(jù)訪問時局部性高一點,一次可以讀入更多的數(shù)據(jù),以這種方式來提高數(shù)據(jù)訪問的效率。 這里我想再強調(diào)一下,根據(jù)我們在GPU上進行性能優(yōu)化的工作體會,在大部分情況下,將CPU的代碼移植到GPU上的時候,數(shù)據(jù)結(jié)構(gòu)都需要進行重組,如果不進行數(shù)據(jù)結(jié)構(gòu)的重組,往往對性能會帶來負面的影響,而且GPU上的性能優(yōu)化有很大的程度是在優(yōu)化對數(shù)據(jù)的訪問,就是如何才能更有效、更快地得到一些數(shù)據(jù)。 還有一個優(yōu)化的方面就是使用Stream(流),我們編寫了很多不同的核心,通過這些核心把它們形成Stream,由于GPU的計算能力很強,通過Stream這種方式,讓它們可以共享GPU資源,提高GPU資源的利用率。 這也是一種比較通用的提高GPU上代碼性能的方式。由于現(xiàn)在GPU的能力越來越強,單個的Kernel不可能充分地利用GPU的資源,因此在很多情況下,Kernel都不能把GPU上的資源用滿,這時利用Stream的優(yōu)化方法去提高GPU資源的利用率也是非常重要的。 ![]() 上圖是對通信優(yōu)化的結(jié)果,我們對大型應(yīng)用程序進行通信優(yōu)化,最主要的一點,也最有效的一點就是用GPU NVLink的特點進行優(yōu)化,就是GPU和GPU之間可以不通過CPU直接進行數(shù)據(jù)交換。我們在測試以后發(fā)現(xiàn)這種方式對性能的提高非常大。 ![]() 這個應(yīng)該是目前世界上最大的GPU集群,它是由美國橡樹嶺國家重點實驗室在Titan GPU集群上測試的結(jié)果??偟膩碚f與在CPU上MPI并行版本相比,性能提高了2到3倍。
Q&A環(huán)節(jié) 問題 都志輝:1,其實我在前面講的時候也大概說了一下,對一個大型應(yīng)用程序在GPU上并行加速的時候,不要直接就往上移植,最好是先好好分析一下這個程序,先把問題、結(jié)構(gòu)和流程搞清楚。然后再把你往GPU移植所要采取的手段和方法也想清楚,之后再動手。我們一般說性能優(yōu)化,包括三個方面:應(yīng)用、算法和硬件體系結(jié)構(gòu)。那么移植也差不多,先把應(yīng)用搞明白,再去移植,然后你再考慮如何用算法來實現(xiàn),最后再結(jié)合GPU和硬件來做這件事情。 2,這個問題比較通用,干我們這一行的很多人都有同感。我要給你一點建議是:其實對于我們一開始做優(yōu)化的人來說,可能上來的想法是我如何把性能提高就行了,如果你想在博士期間做這件事情,想發(fā)論文,想找創(chuàng)新點,我覺得不是一上來就想著怎么去優(yōu)化的問題,而是要先多想想問題的本質(zhì)在哪里,別人是怎么做的,為什么不行,你打算怎么做,這個方法為什么可以或者你覺得他為什么有可能不行,在優(yōu)化之前或者是優(yōu)化過程中,如果你把這些問題都弄明白了,那么你動手有了優(yōu)化結(jié)果后,也就意味著你寫的文章或者是你的創(chuàng)新點也比較容易找到了。 我們之前也犯過毛病,那時候我們覺得反正有這么一個任務(wù),想著快點完成就行了,然后這樣弄完之后,你只能說我把這性能提高了多少倍,但是有很多問題都沒有想清楚,比如你這么做比別人做有什么好處,為什么不用別的方法,有沒有更好的方法,你這個方法的極限和瓶頸在哪等,可能都沒有想清楚,所以如果想發(fā)論文或者是找創(chuàng)新點,那就在動手之前先多考慮考慮,多問些問題。這就是我的一點建議。 對于權(quán)衡這方面,首先你說的非常對,如何去權(quán)衡目前確實沒有特別好的辦法,只能硬著頭皮去做,在做應(yīng)用領(lǐng)域時就得先把應(yīng)用問題搞清楚,如果搞不清楚就不可能把這個優(yōu)化做好,這是我的一點體會。 |
|