1. 目標:分析迴圈分塊最佳化技術,並分析cache 命中情況
假設每個cacheline可以儲存b個資料元素。
2. 原始碼分析
for( int i=0;i<N;i++)
{
for(int j=0;j<M;j++)
{
A[i] += B[j];
}
}
cache miss分析:
對A總訪問次數為 NM,每次訪存載入一個cacheline 可以載入b個元素,並且連續訪問,該cacheline所有元素在依次被訪問前不會被替換掉,共需訪存載入cacheline N/b次,每次載入都有一次miss,所以A共有N/b次miss;對B共訪問 NM次,當M足夠大時,cache無法裝載整個陣列B,內層一次迴圈需訪存M/b次,N個迴圈共訪存載入cacaheline NM/b次,每載入一次cacheline有一次miss,所以B在全部迴圈中 miss NM/b。總共 N/b + NM/b次 miss。
3. 迴圈分塊最佳化
原理:原始碼中對B的訪問miss過高,B cacheline被反覆載入,考慮提高B cacheline 載入一次的利用效率,可考慮每載入一次,完成相應所有A元素的訪問,假設L1 cache可以滿足 A所有元素 cacheline 遍歷和 若干組 B cacheline 的計算需求;載入 B cacheline 組的數量不應太大,假設裡面包含了 T 個元素應有 T=nb , T,b << M,N。保證訪問B[T-1]時,B[0]仍在 cache 中。
程式碼思路:對高miss 資料的訪問索引分塊,作為最外層迴圈控制變數,並在最內層迴圈控制塊內迴圈。
for(int j=0;j<M;j+=T)
{
for(int i=0;i<N;i++)
{
for(int t=0;t<T;t++)
A[i] += B[j];
}
}
cache miss分析:
A元素索引i的訪問在內2層迴圈,每次迴圈共訪問N次,每次訪問一個cacheline有一個misss,所以每次迴圈有N/b個不命中,由於j是分塊訪問,共有 M/b個迴圈,所以A元素miss為 N/b *(M/T);B元素的訪問最內層迴圈每次迴圈有 T/b個miss,T的取值為b的倍數並且不能太大,當訪問B[T-1]時B[0]仍在cache中,對於第二層i的遍歷,此時B[j]元素都在cache中,不影響第三層B元素 miss 數量,共有 M/T 次迴圈,所有B元素miss為 T/b *(M/T) = M/b。總共 MN/bT + M/b 次 miss。
4. 對比總結
分塊前後AB全部miss比為(N/b+MN/b)/ (MN/bT + M/b) ,當MN足夠大時,計算極限得 T。
分塊後 A的miss增加,B的miss減少,總misss數為原來的 1/T。