Използвам Intel PCM за фини измервания на процесора. В моя код се опитвам да измеря ефективността на кеша.
По принцип първо поставям малък масив в L1 кеша (като го обхождам много пъти), след това задействам таймера, преминавам през масива още веднъж (което се надявам да използва кеша) и след това изключвам таймера.
PCM ми показва, че имам доста висок L2 и L3 miss ratio. Проверих също с rdtscp
и циклите на операция с масив са 15 (което е много по-високо от 4-5 цикъла за достъп до L1 кеш).
Това, което бих очаквал, е, че масивът е поставен изцяло в L1 кеш и не бих имал висок L1, L2 и L3 коефициент на пропуски.
Моята система има 32K, 256K и 25M съответно за L1, L2 и L3. Ето моят код:
static const int ARRAY_SIZE = 16;
struct MyStruct {
struct MyStruct *next;
long int pad;
}; // each MyStruct is 16 bytes
int main() {
PCM * m = PCM::getInstance();
PCM::ErrorCode returnResult = m->program(PCM::DEFAULT_EVENTS, NULL);
if (returnResult != PCM::Success){
std::cerr << "Intel's PCM couldn't start" << std::endl;
exit(1);
}
MyStruct *myS = new MyStruct[ARRAY_SIZE];
// Make a sequential liked list,
for (int i=0; i < ARRAY_SIZE - 1; i++){
myS[i].next = &myS[i + 1];
myS[i].pad = (long int) i;
}
myS[ARRAY_SIZE - 1].next = NULL;
myS[ARRAY_SIZE - 1].pad = (long int) (ARRAY_SIZE - 1);
// Filling the cache
MyStruct *current;
for (int i = 0; i < 200000; i++){
current = &myS[0];
while ((current = current->n) != NULL)
current->pad += 1;
}
// Sequential access experiment
current = &myS[0];
long sum = 0;
SystemCounterState before = getSystemCounterState();
while ((current = current->n) != NULL) {
sum += current->pad;
}
SystemCounterState after = getSystemCounterState();
cout << "Instructions per clock: " << getIPC(before, after) << endl;
cout << "Cycles per op: " << getCycles(before, after) / ARRAY_SIZE << endl;
cout << "L2 Misses: " << getL2CacheMisses(before, after) << endl;
cout << "L2 Hits: " << getL2CacheHits(before, after) << endl;
cout << "L2 hit ratio: " << getL2CacheHitRatio(before, after) << endl;
cout << "L3 Misses: " << getL3CacheMisses(before_sstate,after_sstate) << endl;
cout << "L3 Hits: " << getL3CacheHits(before, after) << endl;
cout << "L3 hit ratio: " << getL3CacheHitRatio(before, after) << endl;
cout << "Sum: " << sum << endl;
m->cleanup();
return 0;
}
Това е резултатът:
Instructions per clock: 0.408456
Cycles per op: 553074
L2 Cache Misses: 58775
L2 Cache Hits: 11371
L2 cache hit ratio: 0.162105
L3 Cache Misses: 24164
L3 Cache Hits: 34611
L3 cache hit ratio: 0.588873
РЕДАКТИРАНЕ: Проверих и следния код и все още получавам същите коефициенти на пропуски (които бих очаквал да получат почти нулеви коефициенти на пропуски):
SystemCounterState before = getSystemCounterState();
// this is just a comment
SystemCounterState after = getSystemCounterState();
РЕДАКТИРАНЕ 2: Както бе предложено от един коментар, тези резултати може да се дължат на натоварването на самия профилиращ. Така че вместо само веднъж, промених кода, обхождащ масива много пъти (200 000 000 пъти), за да амортизирам режийните разходи на профилиращия. Все още получавам много ниски коефициенти на кеш L2 и L3 (%15).