他被交辦一個任務:把局裡那套超過25萬行、跑了二十年的Fortran颱風模擬程式(CReSS)搬到GPU上執行,好讓颱風路徑預報能更快跑出來,替防災應變多爭取一點時間。
這篇論文做的事情,講白話就是:研究團隊找了一套跑了超過二十年、程式碼超過25萬行的老牌颱風模擬系統,叫做CReSS,是用Fortran一種從1950年代就有、至今仍在氣象、物理等科學運算領域大量使用的老式程式語言寫的。他們請一個AI代理(AI agent)不是只會聊天回答問題的AI,而是可以自己讀程式碼、自己動手寫、自己跑測試、看結果不對再回頭修正的AI工作流程幫忙,把裡面計算最吃重的部分,從原本用OpenMP一種讓程式碼可以「開很多個分身」同時利用電腦裡多顆CPU核心一起算的技術標記寫的CPU平行運算,改寫成可以丟給GPU一種原本是設計來畫3D遊戲畫面的晶片,因為擅長同時做大量簡單重複的運算,現在被拿來加速科學模擬執行的OpenACC跟OpenMP概念很像的技術標記,但目標是把運算交給GPU執行,而不是CPU寫法。
為什麼這件事沒有聽起來那麼簡單?因為像CReSS這種老程式,不只是「一堆能跑的程式碼」而已,它是一項科學資產——過去二十年裡無數次跟真實觀測資料比對、被拿去做研究和防災決策,累積出來的可信度全都寫在這25萬行程式碼裡。GPU搬遷如果只顧「跑得快」,卻不小心讓某個計算的答案悄悄跑掉,這套系統的可信度就會出問題,而且不會像當機那樣一眼看穿——颱風路徑預報可能看起來一切正常,其實已經算錯了。
所以這篇論文的核心方法叫做「驗證優先(validation-centric)」的搬遷工作法:先讓AI代理從舊程式碼裡找出已經平行化的區塊,再拿真實模擬案例跑到一半、有物理意義的資料狀態存成檔案,當作「正確答案」的基準;GPU版本算出來的每一個數字,都要跟這份基準逐點比對,而不是只看「程式有沒有正常跑完」。
最後的成果:用一次真實的颱風模擬案例實測,這套工作法讓162個運算核心都通過了數值驗證,整個應用程式跑出5.1倍的加速;更值得注意的是,驗證過程中意外抓到5個運算核心的計算結果對不上原本答案,追查下去發現是浮點數(floating point)電腦儲存小數點數字的方式,因為儲存空間有限,每次計算都可能產生極小的誤差和內建函式(intrinsic function)程式語言或硬體內建好的數學運算功能,像sin、cos、sqrt這類,不同硬體平台的實作方式可能有些微差異造成的,包括分支發散(branch divergence)當程式跑到「如果...就...否則...」的判斷式時,因為浮點數誤差讓本來該走某一邊的計算被誤判走到另一邊,導致後面的結果完全不同和消去效應(cancellation effect)兩個很接近的數字相減時,微小的誤差會被放大成看起來很大的誤差,是科學計算裡常見的精度陷阱這兩種典型的浮點數陷阱。
| 比較項目 | 一般常見的AI搬程式做法 | 這篇論文的驗證優先工作法 |
|---|---|---|
| 怎麼確認搬對了 | 看程式碼順不順眼、跑幾個小範例測試 | 把跑到一半的真實物理資料存成檔案,對162個運算核心逐一比對計算結果 |
| 遇到誤差怎麼處理 | 只要程式能跑、結果看起來「差不多」就放行 | 誤差超過門檻就打回去重寫,甚至回報給原始開發者確認是不是老程式碼本身也有問題 |
| 驗證範圍 | 通常只測「整個系統跑不跑得動」 | 先測個別運算核心對不對,再測真實颱風模擬案例的應用程式層級結果對不對 |
| 最後產出 | 「能跑的GPU版本」 | 「162個通過數值驗證的GPU運算核心 + 5.1倍加速 + 5個回報給開發者的隱藏誤差」 |
這段程式示範論文核心方法的簡化版:怎麼用「逐點比對」的方式,驗證GPU版本算出來的結果跟CPU原始版本是不是同一件事,而不是只看程式有沒有跑完。
import numpy as npreference = np.load("cpu_reference_dump.npy")gpu_output = np.load("gpu_kernel_output.npy")absolute_diff = np.abs(gpu_output - reference)relative_diff = absolute_diff / (np.abs(reference) + 1e-12)tolerance = 1e-5bad_points = np.where(relative_diff > tolerance)if len(bad_points[0]) > 0: print(f"發現 {len(bad_points[0])} 個網格點誤差超標,需要人工檢查")else: print("這個運算核心通過驗證,可以放心讓GPU版本上線")