小弟最近正在研究數值演算法
順利的話,希望能將程式回饋給LAPACK
在此之前我主要進行C/C++/Python的程式設計
Fortran是我在研究數值演算法的同時,順帶一起學的
我的分析以 Fortran 90 為主,並且遵循 LAPACK 設計守則
比較對象包含C/C++/Python
最大的優點,就是多維資料及定址
real, intent(in) :: A(LDA, *)
可以輕鬆的使用 A(i, j) 來定址資料
在Fortron 90 甚至可以進行向量版本的計算
例如 y(1:n) = a * x(1:n) + y(1:n)
只要加入 -mavx2 或是 -mavx512f 就能輕鬆編譯出使用SIMD指令集的程式
https://godbolt.org/z/EPxsPsdoW
numpy 雖然也有支持 a[1:m, 1:n] 這樣的定址模式
但我們很難期待它有高效能
C/C++ 對應同樣的資料結構,就必須寫 A[j*lda + i],非常的不方便
一不小心弄錯i, j,程式就會出錯
需要多寫一層迴圈也會影響程式的閱讀性
這個獨特的優點直接壓過所有的缺點,使得它成為設計數值程式的首選
其語法與matlab, numpy類似
卻有C/C++的速度
如果是作為數值演算法的使用者,那麼numpy/scipy/matlab等都是好選擇
但如果是要作為數值演算法的實作者,fortran的效能還是上述語言無法取代的
可惜的是Fortran讓人惱人的缺點也不少
1. 無法內嵌組合語言
高效能需求的矩陣乘法函式庫BLAS,效能好的都已經改用C+組合語言來撰寫
畢竟編譯器能提供的幫助有限,少數的地方我們還是希望能精準的控制組合語言的輸出
一個算是重視效能的語言卻禁止結合組合語言,我覺得其決策弊大於利
(官方說法是Fortran的設計初衷就是不綁定機器)
連rust都能使用assembly intrinsics了
對於習慣使用intrinsics來加速的我來說,這個禁令非常的綁手綁腳
2. 缺乏標準測試環境
作為軟體工程師,我的開發習慣從設立一個測試環境開始
不論是測試自己對語法的熟悉度、函式庫使用起來是否正確
或是一些用來驗證開發中程式內部結構的程式,都非常的好用
我一開始是從fortran維基上找
https://fortranwiki.org/fortran/show/Unit+testing+frameworks
我不知道別人實測的心得如何
我自己是覺得相當難以上手
所以我早期的測試程式是幫fortran寫c header file後
再以google test來進行測試
後來乾脆自己開發一個與cmake結合的版本:
https://github.com/dryman/fortran_unit_test
它符合多數程式語言在寫測試時的習慣
也就是一個測試就是一個function
然後測試環境會依序測試每個function是否正確執行
3. 與主流程式語言不同的語意
例如不能使用 IF (A .and. B) ,在 A 為非的時候不執行 B
(no short circuit)
不能使用 X = A ? B : C; 這種在一般CPU都有特殊的優化組語 (conditional move)
不過fortran 2023就真的可以寫
value = ( a > 0.0 ? a : 0.0)
4. 與 C 的程式作連結時,字串處理的問題
在我撰寫CMake/Ctest for fortran時,最讓我苦惱的問題是處理字串
在 C 當中,我們使用int argc, char** argv來傳遞arguments
然而在fortran當中,要將char**轉成fortran容易處理的資料,卻相當痛苦
最簡單的選項,是將其變為array of size-1 array, each contain a character
但是這樣的資料卻又沒辦法丟給其他的fortran字串處理程式
例如ctest其實支援傳遞parameterized arguments
./my_test test_fn 50 100
50 和 100 會被當作 5, 1
我最後的解法是把C stdlib的atoi搬進fortran來使用
這問題在fortran 2023似乎也能解決:
US 09 會增加 call c_f_strpointer
5. 變數宣告必需要在程式開頭
這點也是對開發相當冗餘的設計
不過看起來沒有機會改善 :(
寫了這麼多,對缺點的抱怨多過優點
不過實際上我是真的蠻感激fortran 90的語法對開發數值程式的幫助
要在其他語言上開發相同的功能,其實更加不容易
以上就是我的心得,也歡迎對程式語言及數值程式有興趣的朋友分享想法