Warning
This page is located in archive. Go to the latest version of this course pages. Go the latest version of this page.

2. Profilování kódu

Na začátku cvičení se vrátíme k verzovacímu systému Git a vyzkoušíme si vytváření a spojování větví. Potřebné příkazy najdete třeba na stránce GIT (2/2) - přehled základních příkazů

Analýza volání funkcí

Pro analýzu volání funkcí použijeme následující kód:

#include <valgrind/callgrind.h>
 
int add() {
    int val = 0;
 
    for(int i = 0; i < 800; ++i)
        val += i;
 
    return val;
}
int mult() {
    int val = 1;
 
    for(int i = 0; i < 800; ++i)
        val *= i;
 
    return val;
}
 
int foo() {
    return add() + add() + mult();
}
 
int main(void) {
    int dum = 0;
 
    CALLGRIND_START_INSTRUMENTATION;
    for(int i = 0; i < 80000; ++i)
        dum += foo();
    CALLGRIND_STOP_INSTRUMENTATION;
 
    return 0;
}

Jednoduchý nástroj na analýzu volání funkcí se jmenuje gprof a je součástí balíčku binutils. Aby bylo možné nástroj využít, je třeba kompilovat zdrojový kód s volbou -pg. Po spuštění takto kompilovaného prorgramu se vytvoří soubor gmon.out, který obsahuje informace pro gprof.

gcc -Wall -pg call.c -o call
./call 
gprof call gmon.out > gpref_res

Druhým nástrojem je callgrind, součást valgrindu.

valgrind --tool=callgrind --dump-instr=yes --instr-atstart=no ./call

Při volání vznikne log, který je možné analyzovat vhodným nástrojem, např. qcachegrind.

Analýza přístupu do paměti

Moderní procesory nepřistupují do operační paměti přímo, ale přes dočasné paměti (cache), díky kterým lze za příznivých podmínek významně urychlit získání dat.

Pro testy budeme používat program, který náhodně vybírá prvky předdefinovaného pole

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
 
#define SIZE    10485763  
#define NBR     10000
 
int main() 
{
    srand(time(NULL));
 
    // array of data
    int* a = malloc(SIZE*sizeof(int));
 
    for (int i = 0; i < SIZE; i++)
        a[i] = i;
 
    // array of random index
    int* index = malloc(NBR*sizeof(int));
 
    for (int k = 0; k < NBR; ++k)
        index[k] = rand() % SIZE;
 
    int d = 0;
 
    for (int k = 0; k < 1000; ++k) {
        int c = 0;
 
        // random fashion access
        for (int n = 0; n < NBR; ++n)
            c += a[index[n]];
 
        d += c;
    }
 
    printf("%i\n", d);
 
    free(a);
    free(index);
 
    return 0;
}

V Linuxu se tradičně používá nástroj perf, který je součástí balíčku linux-tools. Pokud používáte WSL, je třeba zkompilovat nástroj ze zdrojového kódu upraveného jádra Linuxu:

sudo apt install build-essential flex bison libssl-dev libelf-dev
git clone --depth=1 https://github.com/microsoft/WSL2-Linux-Kernel.git
cd WSL2-Linux-Kernel/tools/perf
make

V prostředí WSL ale zdá se není analýza plně podporována :-(

Proto vyzkoušíme nástroj cachegrind, který je opět integrován ve valgrindu.

valgrind --tool=cachegrind --cache-sim=yes  ./cache

Pro urychlení práce můžete využít třeba následující makefile:

call: call.c
	$(CC) -g call.c -o call

cache: cache.c
	$(CC) -g cache.c -o cache

callgrind:
	valgrind --tool=callgrind --dump-instr=yes --instr-atstart=no ./call

cachegrind:
	valgrind --tool=cachegrind --cache-sim=yes  ./cache

perf:
	perf stat -ddd ./cache

clean:
	rm -f call cache *grind.out.* 

courses/b2b99ppc/tutorials/02.txt · Last modified: 2021/02/23 14:26 by viteks