1. 程式人生 > >以矩陣乘法為例,瞭解cpu cache對程式效能的影響

以矩陣乘法為例,瞭解cpu cache對程式效能的影響

/*square1.cpp*/
/*未經優化的矩陣乘法程式*/
#include 
using namespace std;
#define N 1000
int a[N][N] = {0}, b[N][N] = {0}, c[N][N] = {0};
int main() {
    int i, j, k;
    for (i = 0; i < N; i++) {
        for (j = 0; j < N; j++) {
            a[i][j] = i+j;
            b[i][j] = i+j;
        }
    }
    for (i = 0; i < N; i++) {
        for (j = 0; j < N; j++) {
            for (k = 0; k < N; k++) {
                c[i][j] += a[i][k] * b[k][j];
            }

        }
    }
}
/*square2.cpp*/  
/*優化過的矩陣乘法程式*/  
#include   
using namespace std;  
#define N 1000  
int a[N][N] = {0}, b[N][N] = {0}, c[N][N] = {0};  
int main() {  
    int i, j, k;  
    for (i = 0; i < N; i++) {  
        for (j = 0; j < N; j++) {  
            a[i][j] = i+j;  
            b[i][j] = i+j;  
        }  
    }  
    for (i = 0; i < N; i++) {  
        for (k = 0; k < N; k++) {  
            for (j = 0; j < N; j++) {  
                c[i][j] += a[i][k] * b[k][j];  
            }  
  
        }  
    }  
}
兩段程式的唯一差別,就是把 三層迴圈中的 j 迴圈 和k 迴圈的順序交換了一下。

square1.cpp中因為第三層迴圈(最內層迴圈)是對k進行迴圈,因此b[k][j]是對b逐列進行訪問。我們知道記憶體中二維陣列是以行為單位連續儲存的,逐列訪問將會每次跳1000*4(bytes)。根據cpu cache的替換策略,將會有大量的cache失效。

因此square2.cpp將j迴圈和k迴圈交換位置,這樣就保證了

c[i][j] += a[i][k] * b[k][j];

這條語句對記憶體的訪問是連續的,增加了cache的命中率,大大提升了程式執行速度。

我們來看一下實測效果:(測試環境:64位雙核2.4GHz cpu)

執行時間測試:


時間居然會相差近10倍。 可見利用好cpu cache優化我們的程式,是非常有必要掌握的技能。

平時寫程式時,也應當儘量使cpu對記憶體的訪問,是儘可能連續的。