劉穎輝, 遲學(xué)斌, 姜金榮, 張 峰
1(中國(guó)科學(xué)院 計(jì)算機(jī)網(wǎng)絡(luò)信息中心, 北京 100190)
2(中國(guó)科學(xué)院大學(xué), 北京 100049)
隨著計(jì)算機(jī)科學(xué)的發(fā)展, 不同特色的程序語(yǔ)言不斷涌現(xiàn), 由于Fortran 更接近數(shù)學(xué)語(yǔ)言, 執(zhí)行效率較高,有著較低的開(kāi)發(fā)成本, 因此在數(shù)值計(jì)算、科學(xué)和工程技術(shù)領(lǐng)域, Fortran 依舊占據(jù)主流地位; 大量的數(shù)值計(jì)算工程軟件, 開(kāi)發(fā)語(yǔ)言同樣仍以Fortran 為主. 但在氣候、流體力學(xué)等多個(gè)領(lǐng)域的模擬過(guò)程中, 隨著計(jì)算規(guī)模的擴(kuò)大以及對(duì)于計(jì)算精度的更高要求, 程序計(jì)算所需的時(shí)間也在不斷延長(zhǎng), 部分大型程序需要幾天甚至幾十天才能完成計(jì)算. 傳統(tǒng)的優(yōu)化方式是將Fortran 程序與MPI 進(jìn)行結(jié)合, 通過(guò)實(shí)現(xiàn)粗粒度的并行來(lái)提高計(jì)算效率. 近些年來(lái), 隨著高性能計(jì)算機(jī)的迅速發(fā)展, 通過(guò)GPU 和CPU 的異構(gòu)計(jì)算平臺(tái)[1]來(lái)實(shí)現(xiàn)細(xì)粒度的并行這一課題成為了學(xué)術(shù)界和工業(yè)界的研究熱點(diǎn). 基于Fortran 所開(kāi)發(fā)的科學(xué)計(jì)算軟件及程序, 通過(guò)異構(gòu)并行來(lái)實(shí)現(xiàn)性能的巨大提升, 在工程上有著急切的需求. 在異構(gòu)計(jì)算平臺(tái)上, 以用于GPU 編程的CUDA C 語(yǔ)法為主流的行業(yè)標(biāo)準(zhǔn), AMD 平臺(tái)使用的ROCm 語(yǔ)法也參照CUDA C 規(guī)范. 但是從Fortran 到CUDA C 的移植過(guò)程, 不僅要求開(kāi)發(fā)人員對(duì)Fortran 與CUDA C 均有較高的熟悉度, 而且在復(fù)雜的文件和模塊中檢索相關(guān)變量的信息并編寫(xiě)相應(yīng)CUDA 函數(shù)往往有著巨大的工程量, 如果完全通過(guò)人工完成, 不僅效率低下、極易出錯(cuò), 且后期難以維護(hù)和調(diào)試. 因此從Fortran 到CUDA C 的自動(dòng)轉(zhuǎn)碼工具, 有著極大的需求.
現(xiàn)如今僅有一些將C 語(yǔ)言轉(zhuǎn)換為CUDA 的工具,加拿大多倫多大學(xué)的Han 等人設(shè)計(jì)了一種基于指令的CUDA 編程語(yǔ)言hiCUDA[2], 西安交大的Li 等人實(shí)現(xiàn)了一種源到源自動(dòng)并行化工具GPU-S2S[3], 孫香玉提出了一種面向 CUDA 的源到源并行化架構(gòu) STSCUDA[4], 三者都是用于實(shí)現(xiàn)C 語(yǔ)言到CUDA 的自動(dòng)轉(zhuǎn)換, 但對(duì)于大型計(jì)算程序來(lái)說(shuō), 需要人工檢索程序中大量變量的相關(guān)信息, 再對(duì)內(nèi)存的分配拷貝釋放等諸多操作插入編譯指導(dǎo)語(yǔ)句. 通過(guò)這種方式來(lái)進(jìn)行轉(zhuǎn)化所需的工作量同樣是十分巨大的. 荷蘭埃因霍芬理工大學(xué)的Nugteren 等人設(shè)計(jì)了自動(dòng)轉(zhuǎn)換編譯器Bones[5],Bones 是一個(gè)概念性的驗(yàn)證而非工業(yè)性的編譯器, 其側(cè)重點(diǎn)在于對(duì)特定結(jié)構(gòu)的程序進(jìn)行轉(zhuǎn)換以及自動(dòng)優(yōu)化,對(duì)于實(shí)際程序中的復(fù)雜情況不能很好的支持. 除此之外, 部分學(xué)者設(shè)計(jì)了與CUDA 轉(zhuǎn)化相關(guān)的編譯器[6-8],取得了不錯(cuò)的效果. 而從Fortran 向CUDA C 的轉(zhuǎn)化工具, 僅美國(guó)國(guó)家海洋和大氣管理局地球系統(tǒng)研究實(shí)驗(yàn)室開(kāi)發(fā)了一種名為F2C-ACC 的編譯器[9], 但作者并未給出具體實(shí)現(xiàn)細(xì)節(jié)以及實(shí)際效果.
正則表達(dá)式是對(duì)字符串進(jìn)行邏輯過(guò)濾的一種邏輯操作[10]. 將某類(lèi)字符抽象為一些特定的字符, 通過(guò)某些特定字符的組合來(lái)描述字符串匹配的模式.
正則表達(dá)式具有強(qiáng)大的靈活性與功能性, 僅需非常簡(jiǎn)便的代碼就可以實(shí)現(xiàn)復(fù)雜的字符串操作. 通過(guò)正則表達(dá)式, 可以實(shí)現(xiàn)如下功能: (1)匹配. 判斷目標(biāo)字符串是否與描述的模式相匹配; (2)獲取. 從字符串中提取需要的特定信息; (3)編輯. 對(duì)字符串的子集進(jìn)行切割、替換等操作.
shell 腳本是一種為 shell 編寫(xiě)的腳本程序, 能夠輕易的處理文件與目錄之類(lèi)的對(duì)象. 通過(guò)結(jié)合正則表達(dá)式以及sed、grep、awk 等命令可以在短時(shí)間內(nèi)完成一個(gè)功能強(qiáng)大又好用的腳本, 如下代碼實(shí)現(xiàn)了獲取使用操作符“( )”的變量名的功能:
如果采用C 語(yǔ)言編寫(xiě)實(shí)現(xiàn)上述功能, 可能需要幾十行甚至上百行的代碼, 由此可見(jiàn)shell 腳本在字符串處理方面的強(qiáng)大功能.
Fortran 與CUDA C 在語(yǔ)法規(guī)范、內(nèi)存分配等方面均有較大的差異, 直接從Fortran 轉(zhuǎn)換為CUDA C 有著較大的難度. 而C 語(yǔ)言與Fortran 無(wú)論是從結(jié)構(gòu)方面還是語(yǔ)法的角度都比較相似, 同時(shí)在CUDA C 程序中,運(yùn)行在主機(jī)端的代碼仍然采用C 語(yǔ)言進(jìn)行編寫(xiě), C 語(yǔ)言很好的建立了從Fortran 到CUDA C 的橋梁. 因此將Fortran 程序轉(zhuǎn)換為CUDA C 的過(guò)程分為如下兩部分: (1) Fortran 語(yǔ)言到C 語(yǔ)言的轉(zhuǎn)換; (2)從C 語(yǔ)言轉(zhuǎn)化為CUDA C. 結(jié)合Fortran 與CUDA C 兩種語(yǔ)言的特點(diǎn), 轉(zhuǎn)換過(guò)程需要解決如下問(wèn)題:
(1) 數(shù)組的處理. Fortran 中數(shù)組下標(biāo)可以采用形如A(index1:index2)形式來(lái)自定義起始下標(biāo)與終止下標(biāo), 若采用默認(rèn)定義, 則起始下標(biāo)為1[11]; 而在C 語(yǔ)言中所有數(shù)組下標(biāo)均從0 開(kāi)始, 且不支持自定義的方式來(lái)訪問(wèn)元素. 對(duì)于多維數(shù)組, Fortran 采用列優(yōu)先的方式進(jìn)行存儲(chǔ), 而C 語(yǔ)言為行優(yōu)先. 因此如何完成下標(biāo)映射將成為轉(zhuǎn)換過(guò)程中的一大難點(diǎn). 對(duì)于動(dòng)態(tài)分配的數(shù)組,定義中的維度用“:”來(lái)進(jìn)行, 需要額外檢索allocate 語(yǔ)句來(lái)獲取對(duì)應(yīng)的數(shù)組維度. Fortran 對(duì)數(shù)組的訪問(wèn)也更加靈活, 如對(duì)于維度相同的一維數(shù)組A、B, 則可以直接通過(guò)A(:)=B(:)來(lái)完成對(duì)應(yīng)元素的賦值. 因此對(duì)于使用“:”作為維度的相關(guān)語(yǔ)句需要進(jìn)行較為復(fù)雜的處理.
(2) 引用外部模塊. Fortran90 采用module 將一系列的數(shù)據(jù)與函數(shù)封裝起來(lái), 任何程序都可以通過(guò)use語(yǔ)句來(lái)引用該module 中的內(nèi)容; 而在C 語(yǔ)言中引用其他變量通過(guò)include 或者extern 關(guān)鍵字來(lái)實(shí)現(xiàn). 因此轉(zhuǎn)換過(guò)程中, 文件的檢索范圍不僅限于本模塊, 還需要包含引用的所有外部模塊. 同時(shí)為了將轉(zhuǎn)換后的CUDA C 程序與原有的Fortran 程序鏈接起來(lái), 對(duì)變量的名稱(chēng)需要根據(jù)編譯器進(jìn)行額外的處理, 因此如何將引用的外部模塊轉(zhuǎn)換為同時(shí)兼容C 程序、Fortran 程序的形式將是一大難點(diǎn).
(3) CUDA 函數(shù)的處理. 在異構(gòu)計(jì)算平臺(tái)上, CPU用于控制計(jì)算過(guò)程和實(shí)施存儲(chǔ)策略, 而具體的計(jì)算過(guò)程則由GPU 進(jìn)行[12]. 而在GPU 上不能直接訪問(wèn)CPU端的內(nèi)存, 因此GPU 計(jì)算所需的數(shù)據(jù)以及計(jì)算得到的結(jié)果必須顯式地與CPU 進(jìn)行數(shù)據(jù)的傳輸. 除此之外,在GPU 端執(zhí)行的代碼需針對(duì)并行做相應(yīng)的修改. 因此針對(duì)計(jì)算過(guò)程中的大量變量, 如何自動(dòng)生成函數(shù)實(shí)現(xiàn)內(nèi)存的分配、拷貝以及釋放將是轉(zhuǎn)換過(guò)程中的一大挑戰(zhàn).
針對(duì)以上難點(diǎn), 本文進(jìn)行了相關(guān)算法設(shè)計(jì)和實(shí)現(xiàn).
3.2.1 Fortran 語(yǔ)言到C 語(yǔ)言的轉(zhuǎn)換
在Fortran 到C 語(yǔ)言的轉(zhuǎn)換過(guò)程中, 一方面需要完成相關(guān)語(yǔ)法、關(guān)鍵字等內(nèi)容的轉(zhuǎn)換, 另一方面對(duì)于每個(gè)變量, 需要從本文件或外部模塊中檢索來(lái)建立一張變量信息表, 表中需包含文件名稱(chēng)、變量名稱(chēng)、變量類(lèi)型、維度等轉(zhuǎn)換中必要的信息. 轉(zhuǎn)換流程如圖1 所示.
圖1 Fortran 到C 的轉(zhuǎn)換流程圖
(1) 規(guī)范代碼行
處理源文件中的注釋與跨行. 為保證程序的可讀性, 源程序的注釋不刪除, 將“!”所注釋的內(nèi)容均修改為C 的“//”形式; 刪除跨行符“&”并合并多行為一行, 刪除文件中的空行.
(2) 提取變量信息
1) 數(shù)組型變量
首先檢索源文件中的變量定義語(yǔ)句, 獲取所有的內(nèi)部數(shù)組變量名稱(chēng). 根據(jù)下標(biāo)運(yùn)算符“( )”提取所有可能的數(shù)組變量, 去除關(guān)鍵字、函數(shù)名以及內(nèi)部數(shù)組變量,得到所有外部數(shù)組類(lèi)型的變量名稱(chēng).
通過(guò)對(duì)源文件進(jìn)行檢索, 截取數(shù)組變量的定義或allocate 語(yǔ)句的相關(guān)信息, 建立內(nèi)部數(shù)組變量與相應(yīng)維度、類(lèi)型的映射關(guān)系. 同理, 根據(jù)use 語(yǔ)句提取源文件引用的module 名稱(chēng), 在相應(yīng)文件中查找并建立外部數(shù)組變量信息表.
2) 基本數(shù)據(jù)類(lèi)型變量
由于直接從源文件篩選可能的變量名不僅效率低下, 而且準(zhǔn)確率較低, 因此從引用的模塊中提取出所有的變量定義(不含數(shù)組類(lèi)型變量), 依次進(jìn)行過(guò)濾, 僅保留在源文件中使用的子集, 從而建立外部變量與相應(yīng)類(lèi)型的映射關(guān)系.
(3) 引用模塊轉(zhuǎn)換
對(duì)源文件中引用的每一個(gè)模塊, 生成相應(yīng)的頭文件, 該文件中對(duì)使用到的相關(guān)變量進(jìn)行聲明. 對(duì)于非數(shù)組型變量直接將其轉(zhuǎn)換為C 語(yǔ)言的形式. 對(duì)于數(shù)組型變量, 由于C 程序數(shù)組的處理與Fortran 存在較大差異, 如C 程序數(shù)組維度僅能通過(guò)常量定義, 起始下標(biāo)為0, 不支持自定義下標(biāo)等, 因此將其聲明為同類(lèi)型的指針變量, 通過(guò)一維數(shù)組的方式進(jìn)行訪問(wèn).
(4) 結(jié)構(gòu)轉(zhuǎn)換
1) 關(guān)鍵字以及語(yǔ)句的轉(zhuǎn)換
將Fortran 中的關(guān)鍵字與語(yǔ)句均轉(zhuǎn)換為C 語(yǔ)言中對(duì)應(yīng)的形式, 包括但不限于表1 的內(nèi)容. 注釋在C 程序中無(wú)需使用的語(yǔ)句, 最大限度保證程序的可讀性.
表1 Fortran 與C 關(guān)鍵字及語(yǔ)句對(duì)照表
2) 數(shù)組下標(biāo)索引的轉(zhuǎn)換
對(duì)于Fortran 中定義的數(shù)組, 如real(8) dimension(index1) :: A, 則數(shù)組A 的維度大小為index1, 由于C 語(yǔ)言中起始下標(biāo)默認(rèn)為0, 當(dāng)通過(guò)下標(biāo)來(lái)進(jìn)行元素的訪問(wèn)時(shí)需要減去相應(yīng)的偏移量, 如A(i) 應(yīng)轉(zhuǎn)換為A[i-1]. 更一般的, 對(duì)于Fortran 中自定義下標(biāo)的數(shù)組,如: real(8) dimension(index1:index2) ::B, 則數(shù)組B 的維度大小為index2-index1+1, 相應(yīng)的訪問(wèn)B(i)應(yīng)轉(zhuǎn)換為B[i-index1].
對(duì)于多維數(shù)組, 如Fortran 中定義數(shù)組real(8),dimension (dimx, dimy) :: D, 由于行列優(yōu)先的不同, 對(duì)應(yīng)C 程序的數(shù)組定義為: double D[dimy][dimx] , 相應(yīng)下標(biāo)訪問(wèn)D(i,j)對(duì)應(yīng)C 程序中的D[j-1][i-1]; 根據(jù)上文所述, 將多維數(shù)組均視為一維數(shù)組進(jìn)行相關(guān)運(yùn)算, 下標(biāo)訪問(wèn)需進(jìn)一步轉(zhuǎn)換為D[(j- 1)*dimy+(i-1)].
3) 數(shù)組名稱(chēng)的轉(zhuǎn)換
代碼的前后處理在CPU 上進(jìn)行, 一般使用原有代碼, 這就需要實(shí)現(xiàn)C 和Fortran 的混合編程, 主要是實(shí)現(xiàn)數(shù)組或變量的互相訪問(wèn). 以ifort 編譯器下的文件param.F90 中定義的外部數(shù)組變量b 為例, 將數(shù)組名稱(chēng)b 轉(zhuǎn)換為param_mp_b_, 所有外部數(shù)組變量均要做相應(yīng)的替換.
Fortran 到C 的整體轉(zhuǎn)換算法如算法1 所示.
算法1. Fortran 到C 的轉(zhuǎn)換1. 輸入Fortran 文件2. 預(yù)處理3. 匹配call 語(yǔ)句, 提取函數(shù)名稱(chēng)4. 匹配use 語(yǔ)句, 提取引用模塊名稱(chēng)5. for file in 源文件, 引用模塊文件6. 匹配變量define 語(yǔ)句、allocate 語(yǔ)句7. 存儲(chǔ)變量名稱(chēng)、所在文件、類(lèi)型、維度等信息8. end for 9. for var in 變量信息表10. if var in 引用模塊文件11. //以ifort 編譯器為例12. 替換變量名稱(chēng): var → filename_mp_var_13. end if 14. end for 15. 轉(zhuǎn)換Fortran 關(guān)鍵字、語(yǔ)句為C 形式16. 創(chuàng)建頭文件17. 輸出C 文件
3.2.2 C 語(yǔ)言到CUDA C 的轉(zhuǎn)換
CUDA C 語(yǔ)法規(guī)定用__global__修飾符所修飾的函數(shù)為核函數(shù), 核函數(shù)運(yùn)行在設(shè)備端, 而運(yùn)行在主機(jī)端的代碼與C 語(yǔ)言是完全兼容的. 因此從C 語(yǔ)言到CUDA C的轉(zhuǎn)換僅需考慮在設(shè)備端運(yùn)行的部分代碼即可, 這部分代碼所涉及的數(shù)據(jù)以及得出的結(jié)果均需要在主機(jī)與設(shè)備內(nèi)存間顯式的進(jìn)行復(fù)制[13], 即需要額外生成對(duì)應(yīng)的拷貝函數(shù). 除此之外, 由于核函數(shù)只能在主機(jī)端調(diào)用,設(shè)備端執(zhí)行[14], 因此主機(jī)端需要顯式的進(jìn)行核函數(shù)的調(diào)用, 并傳遞運(yùn)行時(shí)所需參數(shù), 核函數(shù)的內(nèi)容需結(jié)合多線程并行的特點(diǎn)做出相應(yīng)的調(diào)整. 轉(zhuǎn)換流程如圖2 所示.
圖2 C 到CUDA C 的轉(zhuǎn)換流程圖
(1) 內(nèi)存拷貝函數(shù)的生成
1) 確定拷貝對(duì)象
核函數(shù)中參與運(yùn)算的所有數(shù)組變量均需要在設(shè)備端顯式的進(jìn)行內(nèi)存的分配. 通過(guò)查找上文建立的變量信息表確定類(lèi)型與維度, 聲明同類(lèi)型空指針, 分別利用cudaMalloc、cudaFree 進(jìn)行GPU 內(nèi)存的分配與釋放.如參與運(yùn)算的某一數(shù)組定義為real, dimension(m, d):: a,則需生成如下CUDA C 語(yǔ)句:
其中內(nèi)存分配與釋放語(yǔ)句中的CHECK 為宏定義,用于接收cudaError 并輸出提示信息, 保證程序的健壯性.
2) 生成拷貝函數(shù)
以所有外部數(shù)組變量為全集, 檢索源文件中賦值運(yùn)算符“=”, 出現(xiàn)在運(yùn)算符左側(cè)的所有變量將其從設(shè)備端拷貝到主機(jī)端, 相應(yīng)拷貝函數(shù)封裝為test_DtoH(), 出現(xiàn)在運(yùn)算符右側(cè)的變量將其從主機(jī)端拷貝到設(shè)備端,相應(yīng)拷貝函數(shù)封裝為test_HtoD(). 如對(duì)于c(j, i)=c(j,i)+a(j, k)*b(k, i), 則應(yīng)有如下函數(shù)被生成:
extern "C" void test_DtoH(){//從GPU 到CPU 的內(nèi)存拷貝
(2) 核函數(shù)的轉(zhuǎn)換
1) 核函數(shù)范圍的確定
一般情況下, 建議用戶(hù)將核函數(shù)部分放在一個(gè)單獨(dú)的Fortran 文件中, 使用subroutine 或者function 來(lái)將其封裝, 在主程序中通過(guò)call 語(yǔ)句來(lái)對(duì)其進(jìn)行調(diào)用.
同時(shí), 我們?cè)试S手動(dòng)指定核函數(shù)的范圍, 在對(duì)應(yīng)的代碼塊加入如下指導(dǎo)語(yǔ)句:
二者之間的代碼被認(rèn)為是一個(gè)完整的名為test 的核函數(shù).
2) 形參列表的確立
核函數(shù)中所用到的變量均需要作為形參列表的一部分來(lái)進(jìn)行傳遞, 具體分類(lèi)如下: ① 對(duì)于基本數(shù)據(jù)類(lèi)型: 整型、實(shí)型、字符型、邏輯型, 直接將其轉(zhuǎn)換為C 中所對(duì)應(yīng)的類(lèi)型, 作為參數(shù)來(lái)進(jìn)行傳遞. ② 對(duì)于數(shù)組型變量, 將其均視為一維數(shù)組, 相應(yīng)類(lèi)型的指針作為形參來(lái)進(jìn)行傳遞. ③ parameter 關(guān)鍵字定義的常量采用在頭文件中宏定義的方式進(jìn)行處理, 無(wú)需進(jìn)行參數(shù)的傳遞.
3) 函數(shù)體的轉(zhuǎn)換
GPU 上的計(jì)算以kernel 函數(shù)為主, 科學(xué)計(jì)算軟件中主要的計(jì)算代碼是do 循環(huán)計(jì)算(模板計(jì)算), 圖3 給出了模板計(jì)算的kernel 函數(shù)生成. 首先是插入了線程號(hào)計(jì)算代碼, 然后根據(jù)線程號(hào)將循環(huán)計(jì)算任務(wù)分配給相應(yīng)的線程. 由于控制循環(huán)的變量被修改, 因此原先由循環(huán)變量控制的數(shù)組下標(biāo)也要做出相應(yīng)的修改. 在Fortran 程序中由數(shù)組下標(biāo)i 控制的循環(huán), 在循環(huán)展開(kāi)后i 的含義變?yōu)镃UDA C 程序中的線程號(hào). 多維情況與一維情況類(lèi)似, 不再贅述.
圖3 循環(huán)展開(kāi)格式
C 到CUDA C 的整體轉(zhuǎn)換算法如算法2 所示.
算法2. C 到CUDA C 的轉(zhuǎn)換1. 輸入C 文件2. for var in 變量信息表3. if var in 引用模塊文件4. 聲明設(shè)備端相應(yīng)變量, 創(chuàng)建cudaMalloc 函數(shù)5. if var match 賦值語(yǔ)句左側(cè)6. 創(chuàng)建GPU 到CPU 的cudaMemcpy 函數(shù)7. else if var match 賦值語(yǔ)句右側(cè)8. 創(chuàng)建CPU 到GPU 的cudaMemcpy 函數(shù)9. end if 10. 創(chuàng)建cudaFree 函數(shù)11. end if 12. end for 13. 確立形參列表, 生成核函數(shù)14. 轉(zhuǎn)換核函數(shù)函數(shù)體15. 輸出CUDA C 文件
本文實(shí)驗(yàn)環(huán)境如表2 所示.
表2 實(shí)驗(yàn)環(huán)境配置
稠密矩陣的乘法是典型的計(jì)算密集型問(wèn)題, 且具有較好的并行性. 以計(jì)算矩陣乘法:Am×dBd×n=Cm×n為例進(jìn)行測(cè)試, 輸入矩陣A、B均為隨機(jī)生成. 編寫(xiě)名為MatrixMul 的串行矩陣乘法Fortran 程序, 計(jì)算用到的數(shù)組以及相關(guān)變量定義于外部模塊(源程序位于param.F90 中). 基于正則表達(dá)式與shell 語(yǔ)言實(shí)現(xiàn)本文提出的轉(zhuǎn)換算法并進(jìn)行驗(yàn)證, 轉(zhuǎn)換所得結(jié)果與原Fortran 程序計(jì)算結(jié)果相同, 驗(yàn)證了轉(zhuǎn)換方法的正確性.
轉(zhuǎn)換后程序的性能表現(xiàn)如圖4 所示, 其中橫坐標(biāo)是矩陣規(guī)模(m×d×n), 縱坐標(biāo)是計(jì)算時(shí)間. 點(diǎn)狀柱是原MPI 程序在1 個(gè)節(jié)點(diǎn)(2 顆CPU)下的計(jì)算時(shí)間, 條紋柱與灰柱分別對(duì)應(yīng)使用本文算法轉(zhuǎn)換完成和手動(dòng)實(shí)現(xiàn)的CUDA C 代碼在同1 張GPU 卡上的計(jì)算時(shí)間. 從計(jì)算結(jié)果中可以得出, 由本文算法自動(dòng)轉(zhuǎn)換得到的CUDA C矩陣乘法在性能上與人工編寫(xiě)的CUDA C 矩陣乘法相當(dāng), 較原MPI 程序平均加速了1.83 倍. GPU+CPU 的異構(gòu)計(jì)算較原CPU 并行能夠取得更好的加速效果.
圖4 矩陣乘法性能對(duì)比
LICOM 是由中科院大氣物理研究所LASG 國(guó)家重點(diǎn)實(shí)驗(yàn)室發(fā)展的全球海洋環(huán)流模式. 它是中國(guó)科學(xué)院地球系統(tǒng)模式CAS-ESM 的重要組成部分[15].源程序采用Fortran 語(yǔ)言編寫(xiě), 核心計(jì)算程序普遍引用了多個(gè)模塊, 大量的數(shù)組變量通過(guò)allocate 語(yǔ)句來(lái)分配內(nèi)存, 以readyt 為例, 核函數(shù)部分計(jì)算變量累計(jì)86 個(gè), 其中數(shù)組型變量共52 個(gè), 引用自14 個(gè)模塊中數(shù)組變量共40 個(gè), 采用本文算法進(jìn)行代碼自動(dòng)轉(zhuǎn)換, 生成的文件結(jié)構(gòu)及功能如圖5 所示.
圖5 readyt 轉(zhuǎn)換文件結(jié)構(gòu)及功能說(shuō)明
實(shí)驗(yàn)證明: 對(duì)于復(fù)雜的大型程序, 本文提出的轉(zhuǎn)換算法仍能夠自動(dòng)檢索核函數(shù)所需變量, 從對(duì)應(yīng)文件中提取變量的相關(guān)信息, 自動(dòng)生成內(nèi)存分配、拷貝、釋放等一系列的函數(shù), 結(jié)合現(xiàn)有的變量信息表以及轉(zhuǎn)化完成的代碼進(jìn)行調(diào)試, 手動(dòng)修改少量代碼后即可編譯通過(guò), 轉(zhuǎn)化代碼的正確率高達(dá)80%以上, 初步完成了LICOM3 CUDA C 異構(gòu)代碼的開(kāi)發(fā).
進(jìn)一步分析轉(zhuǎn)換算法的輸出結(jié)果, 探究轉(zhuǎn)換過(guò)程中出錯(cuò)的主要原因: (1)計(jì)算中含有冒號(hào)的數(shù)組索引未能進(jìn)行相應(yīng)的展開(kāi); (2) readyt 內(nèi)部定義的部分?jǐn)?shù)組變量在實(shí)際的CUDA C 程序中仍需進(jìn)行額外的處理. 若針對(duì)上述兩點(diǎn)預(yù)先對(duì)Fortran 程序進(jìn)行簡(jiǎn)單的修改, 則轉(zhuǎn)換正確率可達(dá)90%以上.
經(jīng)過(guò)測(cè)試, Fortran 程序中readyt 函數(shù)平均耗時(shí)72.32 s, 轉(zhuǎn)換完成的CUDA C 程序中該函數(shù)平均耗時(shí)1.29 s, 加速了56.06 倍.
本文在深入分析Fortran 語(yǔ)言以及CUDA C 相關(guān)特征的基礎(chǔ)上, 使用正則表達(dá)式和shell 語(yǔ)言, 實(shí)現(xiàn)了一套邏輯清晰、功能強(qiáng)大的Fortran 到CUDA C 的轉(zhuǎn)換方法. 經(jīng)過(guò)測(cè)試, 該方法轉(zhuǎn)換正確率高達(dá)80%以上,轉(zhuǎn)換后的代碼性能與人工編寫(xiě)的CUDA C 代碼相當(dāng),能夠有效節(jié)省大型程序的移植時(shí)間. 本文的實(shí)現(xiàn)存在一定的局限性, 對(duì)于較復(fù)雜的軟件代碼得到的轉(zhuǎn)換結(jié)果仍需開(kāi)發(fā)人員手動(dòng)進(jìn)行調(diào)試, 且實(shí)現(xiàn)的代碼還需要進(jìn)行深度優(yōu)化.