2015-05-16 5 views
0

Я использую 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).

+0

Ваш эксперимент (измеренный цикл while) имеет только 16 итераций. Вероятно, верхние и возмущающие функции getSystemCounterState доминируют в измерениях. – papirrin

+0

Я бы порекомендовал сравнить промахи/хиты L2/LLC с метками попадания L1. Вы можете обнаружить, что у вас мало M L1-хитов против 50K L2 промахов. – Elalfer

ответ

4

кажется, что вы получите l2 и l3 промахов из всех ядер в системе

Я смотрю на реализацию PCM здесь: https://github.com/erikarn/intel-pcm/blob/ecc0cf608dfd9366f4d2d9fa48dc821af1c26f33/src/cpucounters.cpp

[1] в осуществлении PCM::program() на линии 1407 I DON» t видеть какой-либо код, который ограничивает события определенным процессом.

[2] при реализации PCM::getSystemCounterState() в строке 2809 вы можете видеть, что события собраны из всех ядер вашей системы. Поэтому я попытался бы установить сродство процесса к одному ядру, а затем только читать события из этого ядра - с помощью этой функции CoreCounterState getCoreCounterState(uint32 core)

+0

Правильно - PCM по умолчанию получает информацию от всех ядер в вашей системе. – BeeOnRope