我正在開發一個項目,我們必須實現一個算法,該算法在理論上證明是緩存友好的。簡單地說,如果N
是輸入,而B
是每次我們發生緩存未命中時在緩存和RAM之間傳輸的元素的數量,則算法將需要O(N/B)
訪問RAM。爲什麼Perf和Papi爲L3緩存引用和未命中提供了不同的值?
我想證明這確實是實踐中的行爲。爲了更好地理解如何測量各種與緩存相關的硬件計數器,我決定使用不同的工具。一個是Perf,另一個是PAPI庫。不幸的是,我使用這些工具工作得越多,我就越不瞭解他們所做的工作。
我使用的是Intel(R)Core(TM)i5-3470 CPU @ 3.20GHz,8 GB內存,L1緩存256 KB,L2緩存1 MB,L3緩存6 MB。高速緩存行大小爲64個字節。我想這必須是該區塊的大小B
。
讓我們來看看下面的例子:
#include <iostream>
using namespace std;
struct node{
int l, r;
};
int main(int argc, char* argv[]){
int n = 1000000;
node* A = new node[n];
int i;
for(i=0;i<n;i++){
A[i].l = 1;
A[i].r = 4;
}
return 0;
}
每個節點需要8個字節,這意味着高速緩存行可以容納8個節點,所以應當期待大約1000000/8 = 125000
L3高速緩存未命中。
未優化(無-O3
),這是從PERF輸出:
perf stat -B -e cache-references,cache-misses ./cachetests
Performance counter stats for './cachetests':
162,813 cache-references
142,247 cache-misses # 87.368 % of all cache refs
0.007163021 seconds time elapsed
這是非常接近我們期待。現在假設我們使用PAPI庫。
#include <iostream>
#include <papi.h>
using namespace std;
struct node{
int l, r;
};
void handle_error(int err){
std::cerr << "PAPI error: " << err << std::endl;
}
int main(int argc, char* argv[]){
int numEvents = 2;
long long values[2];
int events[2] = {PAPI_L3_TCA,PAPI_L3_TCM};
if (PAPI_start_counters(events, numEvents) != PAPI_OK)
handle_error(1);
int n = 1000000;
node* A = new node[n];
int i;
for(i=0;i<n;i++){
A[i].l = 1;
A[i].r = 4;
}
if (PAPI_stop_counters(values, numEvents) != PAPI_OK)
handle_error(1);
cout<<"L3 accesses: "<<values[0]<<endl;
cout<<"L3 misses: "<<values[1]<<endl;
cout<<"L3 miss/access ratio: "<<(double)values[1]/values[0]<<endl;
return 0;
}
這是我得到的輸出:
L3 accesses: 3335
L3 misses: 848
L3 miss/access ratio: 0.254273
爲什麼兩個工具之間如此大的差異?
同比有嘗試使用計數和PAPI_L3_DCA的PAPI_L3_DCM數據缺失? – HazemGomaa
只有PAPI_L3_DCA可用,它似乎給出了相同的數字 – jsguy