矩陣乘法的優化

    本文轉載在矩陣乘法的優化,其文概要思想便是利用緩存命中率和程序的局部性原理來優化兩個矩陣之間的乘法。原文內容如下。代碼部分的正確性沒有親自驗證。

    矩陣乘法的定義是十分簡單的,如果按照數學上的定義,可以寫出下面的程序:

    從計算機專業的角度來看,上面的代碼是幼稚的。主要表現在:B[k][j]讀取內存中的數據,是不連續的。在最底層的循環中,隨着k不斷加1,B[k][j]不斷的在內存中跳躍。這會引起緩存命中率低,循環程序不斷的把內存轉移至緩存,引起效率降低。

 

    在我的臺式機上,兩個1000階矩陣相乘,需要用時8.5s(用的編譯器是g++ -O3)。

    充分利用計算機系統的特性可以大幅度提高程序性能,在卡內基梅隆大學的鎮校神課《深入理解計算機系統》裏面,給出一種方法,僅僅改變循環的次序,就可以大幅度提高性能,代碼如下:

    首先,最內層的循環,隨着j加1,C[I][J]和B[k][j]都是每次只加1,這符合空間局部性的原理,也就是說,內存每次讀取都是一個接着一個的來,沒有大幅度跳躍。其次,A[i][k]在中間層循環是跳躍的,但是中間層執行的沒有底層那麼多,而且我們把A[i][k]賦給了局部變量r,在編譯器生成彙編代碼的過程中,局部變量r應該由CPU寄存器存儲,最底層循環程序讀取寄存器的時間幾乎可以忽略不計的。

    在我的臺式機上,同樣的兩個1000階矩陣相乘,只需要用時1.57s,是不是很驚人,代碼行數幾乎完全一樣,只改變循環的次序,就有這麼大性能差異。

 

    爲了進一步提高性能,可以將矩陣分塊,每次計算一部分內容。這樣計算時,CPU可以把更規整的把部分內存搬到緩存中,提高緩存命中率。下面是我增加分塊後的代碼:

    在我的臺式機上,同樣的兩個1000階矩陣相乘,只需要用時0.73s,相比於最開始的幼稚程序,性能已經提高1個數量級了,但代碼行數並沒有增加太多。可以得出結論:對於計算機體系結構有所瞭解的程序員寫的程序,與菜鳥程序員相比,性能可能有相當大的差距,雖然工作量可能差不多。

   對於矩陣計算,有成熟的庫可以使用,完全沒必要從頭自己寫代碼。對於高性能的 BLAS實現(線性代數基礎計算)來說,其用到的技術遠遠比本文複雜,需要在分塊計算中減少時間複雜度(Strassen算法),需要自動或手動編寫大量的彙編程序。在我的臺式機上,OpenBLAS執行兩個1000階矩陣相乘,可能只需要用時0.1s。

    但不管怎麼說,爲了寫高性能程序,學習計算機系統的知識絕對有益。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章