Я использую Intel PCM для мелкозернистых измерений ЦП. В моем коде я пытаюсь измерить эффективность кеша.ЦП мер (Кэш-промахи/хиты), которые не имеют смысла
В основном, я сначала помещал небольшой массив в кеш L1 (многократно перебирая его), затем запускаю таймер, перебираю массив еще раз (который, надеюсь, использует кеш), а затем выключая таймер.
PCM показывает мне, что у меня довольно высокий коэффициент пропуска L2 и L3. Я также проверил с 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
EDIT: Я также проверил следующий код, и по-прежнему получать те же отношения несоосности (который я бы ожидали получить почти нулевые коэффициенты промаха):
SystemCounterState before = getSystemCounterState();
// this is just a comment
SystemCounterState after = getSystemCounterState();
EDIT 2: Как было указано в комментарии, эти результаты могут быть связаны с накладными расходами самого профилировщика. Таким образом, я вместо одного раза, я изменил код, пересекающий массив много раз (200 000 000 раз), чтобы амортизировать накладные расходы профайлера. Я все еще получаю очень низкие коэффициенты L2 и L3 Cache (% 15).
Ваш эксперимент (измеренный цикл while) имеет только 16 итераций. Вероятно, верхние и возмущающие функции getSystemCounterState доминируют в измерениях. – papirrin
Я бы порекомендовал сравнить промахи/хиты L2/LLC с метками попадания L1. Вы можете обнаружить, что у вас мало M L1-хитов против 50K L2 промахов. – Elalfer