科學計算實務:軟體、驗證與可重現性

數值軟體堆疊(numerical software stack)

當你在 MATLAB 裡呼叫一行 A \ b、或在 Python 裡呼叫 numpy.linalg.solve(A, b) 來解線性方程組時,它感覺像是一道指令。但在底下,那一行其實是一座高塔的可見塔尖。數值軟體堆疊就是那座塔:一層層的軟體,每一層都建立在它下面那一層之上,合起來把一個友善的高階請求,轉化成真正在你處理器上執行的快速而謹慎的機器指令。你站在最頂層;數十年的細心工程撐住了你腳下的每一層。

由下而上,這些層通常長這樣。最底層的地基是 BLAS(基礎線性代數副程式)——一組小而被極致最佳化的常式,負責向量與矩陣算術(內積、矩陣-向量與矩陣-矩陣乘法),往往針對每種處理器手工調校,以善用快取與向量化(SIMD)單元。在 BLAS 之上坐著 LAPACK,它把笨重的演算法——LU、喬列斯基分解、QR、特徵值、SVD——完全用 BLAS 呼叫堆出來,因而繼承了那份速度。在這些之上,是大多數人真正接觸到的高階環境:MATLAB、NumPy 與 SciPy、Julia、R,以及 Fortran 或 C++ 核心。它們給你易讀的語法、繪圖與資料處理,同時悄悄把真正的算術往下導向 LAPACK 與 BLAS。專門的函式庫(如稀疏求解器 SuiteSparse、特徵值求解器 ARPACK、處理大型平行問題的 PETSc 與 Trilinos)也插進同一個堆疊。

知道這個堆疊存在,是實用考量而非冷知識。第一,效能:同一個運算可能快上十倍或百倍,只因為環境把它派發給了一個最佳化的 BLAS(例如 OpenBLAS 或 Intel MKL),而非一個天真的三重迴圈。第二,信任:底層是整個計算界中被檢視得最嚴格的程式碼之一,數十年來都對著已知答案測試,所以呼叫它們遠比重寫它們安全。第三,除錯:當答案看起來錯了或慢了,知道是哪一層在負責——你的腳本、高階函式庫、LAPACK,還是 BLAS 的建置版本——就告訴你該往哪裡看。誠實的提醒是:這些層藏著假設——某個常式可能默默預期以欄為主(column-major)的儲存、一個對稱矩陣或某一特定精度,而一旦不匹配,就會在沒有任何錯誤訊息的情況下產生錯誤或緩慢的結果。

用三種方式把兩個 2000x2000 的矩陣相乘:純 Python 手寫的三重 for 迴圈要花上幾分鐘;同樣的程式碼用 C 寫快得多;而 numpy 的 A @ B 在不到一秒內完成,因為 @ 派發給了一個最佳化的 BLAS 常式(dgemm),它為快取做分塊(blocking),並使用 CPU 的向量化單元。完全相同的數學,卻是堆疊裡三個截然不同的樓層。

一道高階呼叫往下穿過 LAPACK,派發到一個有快取意識、向量化的 BLAS 核心。

了解這個堆疊也能解釋令人費解的速度差異:同一份 NumPy 安裝,可能慢也可能快,純粹取決於它連結到哪一個 BLAS。決定性的因素往往是那個函式庫,而不是你的程式碼。

又稱
scientific computing stacknumerical library stack數值函式庫堆疊科學計算堆疊