Измервания на процесора (кеш пропуски/удари), които нямат смисъл

Използвам 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).


person narengi    schedule 16.05.2015    source източник
comment
Вашият експеримент (цикълът while, който се измерва) има само 16 повторения. Вероятно режийните разходи и смущенията на функцията getSystemCounterState доминират в измерванията.   -  person papirrin    schedule 16.05.2015
comment
Бих препоръчал да сравните L2/LLC пропуски/попадения с L1 броячи на попадения. Може да откриете, че имате малко M L1 удари срещу 50K L2 пропуска.   -  person Elalfer    schedule 10.07.2015


Отговори (1)


Изглежда, че получавате l2 и l3 пропуски от всички ядра на вашата система

Разглеждам внедряването на PCM тук: https://github.com/erikarn/intel-pcm/blob/ecc0cf608dfd9366f4d2d9fa48dc821af1c26f33/src/cpucounters.cpp

[1] в изпълнението на PCM::program() на ред 1407 Не виждам никакъв код, който да ограничава събитията до конкретен процес

[2] в изпълнението на PCM::getSystemCounterState() на ред 2809 можете да видите, че събитията се събират от всички ядра на вашата система. Така че бих се опитал да задам CPU афинитет на процеса към едно ядро ​​и след това да чета само събития от това ядро ​​- с тази функция CoreCounterState getCoreCounterState(uint32 core)

person effenok    schedule 25.07.2015
comment
Това е правилно - PCM по подразбиране получава информация от всички ядра на вашата система. - person BeeOnRope; 22.06.2016