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

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

 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
/*square1.cpp*//*未經優化的矩陣乘法程式*/#include <iostream>using namespace std;#define N 1000int 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]; } } }}
square1.cpp
  1
  2
  3
  4
  5
  6
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*square2.cpp*/ /*優化過的矩陣乘法程式*/ #include <iostream> 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]; } } } }
square2.cpp 兩段程式的唯一差別,就是把 三層迴圈中的 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對記憶體的訪問,是儘可能連續的