Le processus de développement d'un programme passe par trois étapes
Electric Fence helps you detect two common programming bugs:
Installer le paquet Electric-Fence qui permet de traquer les erreurs d'accès à la mémoire :
sudo apt-get install electric-fence
Compiler ensuite le programme en réalisant l'édition de liens avec Electric Fence :
gcc -c efence.c -ggdb
gcc -o efence.exe efence.o -ggdb -lefence
Il existe plusieurs débogueurs sous Linux :
Compiler ensuite le programme avec -ggdb :
gcc -o my.exe my_program.o -ggdb
Puis lancer ddd sur l'exécutable :
ddd my.exe
Dans la suite de cette section, nous étudions le comportement du programme suivant :
On utilse les options de compilation classiques avec gprof :
> gcc -o prog.exe prog.c -pg
> ./prog.exe
> gprof prog.exe >prog_gprof.txt
Le résultat en sortie peut être visualisé ici.
Le flat profile est le suivant :
Flat profile:
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls s/call s/call name
99.55 21.94 21.94 2000000 0.00 0.00 proc3
0.27 22.00 0.06 2 0.03 0.03 fill_array
0.09 22.02 0.02 2000 0.00 0.01 proc21
0.09 22.04 0.02 2000 0.00 0.01 proc22
0.00 22.04 0.00 2 0.00 0.00 allocate_array
0.00 22.04 0.00 1 0.00 21.98 proc1
On peut donc lire que :
Le second type d'information reporté par gprof concerne la granularité, c'est à dire le temps total passé dans chaque sous-programme ainsi que le nombre d'appels aux autres sous-programmes :
index % time self children called name
[1] 100.0 0.00 22.04 main [1]
0.00 21.98 1/1 proc1 [2]
0.06 0.00 2/2 fill_array [6]
0.00 0.00 2/2 allocate_array [7]
-----------------------------------------------
0.00 21.98 1/1 main [1]
[2] 99.7 0.00 21.98 1 proc1 [2]
0.02 10.97 2000/2000 proc21 [4]
0.02 10.97 2000/2000 proc22 [5]
-----------------------------------------------
10.97 0.00 1000000/2000000 proc21 [4]
10.97 0.00 1000000/2000000 proc22 [5]
[3] 99.5 21.94 0.00 2000000 proc3 [3]
-----------------------------------------------
0.02 10.97 2000/2000 proc1 [2]
[4] 49.9 0.02 10.97 2000 proc21 [4]
10.97 0.00 1000000/2000000 proc3 [3]
-----------------------------------------------
0.02 10.97 2000/2000 proc1 [2]
[5] 49.9 0.02 10.97 2000 proc22 [5]
10.97 0.00 1000000/2000000 proc3 [3]
-----------------------------------------------
0.06 0.00 2/2 main [1]
[6] 0.3 0.06 0.00 2 fill_array [6]
-----------------------------------------------
0.00 0.00 2/2 main [1]
[7] 0.0 0.00 0.00 2 allocate_array [7]
-----------------------------------------------
On voit donc que :
VTune est un outil complet de profiling et de tuning qui permet de déterminer les hot spots et d'éclairer sur leur comportement : instruction peu performante, accès mémoire non aligné, nombre important de défauts de cache dus à des données de trop grande taille.
valgrind est un ensemble d'utilitaires qui servent à débuguer et profiler les programmes
Valgrind is an instrumentation framework for building dynamic analysis tools. There are Valgrind tools that can automatically detect many memory management and threading bugs, and profile your programs in detail. You can also use Valgrind to build new tools.
The Valgrind distribution currently includes six production-quality tools:
It also includes three experimental tools: a stack/global array overrun detector, a second heap profiler that examines how heap blocks are used, and a SimPoint basic block vector generator
Voir également dans la partie C++11 l'utilisation de valgrind avec l'outil memcheck ainsi que ce tutoriel.
On compile le programme à étudier avec l'option de débugage -g puis il suffit de lancer le profilage. Attention cependant car certaines options peuvent augmenter le temps d'exécution d'un facteur 20 à 100 par rapport à une exécution normale.
Dans le cas de l'outil callgrind (équivalent de gprof), on tapera au niveau du shell :
valgrind --tool=callgrind --dump-instr=yes --simulate-cache=yes --collect-jumps=yes <executable> [args...]
Les options --dump-instr=yes --simulate-cache=yes ou --collect-jumps=yes peuvent être supprimées afin de gagner du temps.
Utilisation de kcachegrind pour visualiser les résultats
en sortie de valgrind
Pour obtenir le visuel du call-graph (il faut avoir installé graphviz -- graph visualization software -- sous Ubuntu : sudo apt-get install graphviz), il suffit de cliquer en bas à droite sur l'onglet "Call-graph".
Si seule une partie du code vous intéresse, vous pouvez faire en sorte que valgrind ne se déclenche qu'à partir de l'appel d'un sous-programme, comme sur l'exemple suivant :
Il faudra alors relancer valgrind avec les options --collect-at-start=no et --instru-at-start=no :
valgrind --tool=callgrind --dump-instr=yes --simulate-cache=yes --collect-jumps=yes --collect-at-start=no --instru-at-start=no <executable> [args...]
L'optimisation est parfois difficile à réaliser et demande beaucoup d'efforts afin d'améliorer sensiblement le code (cf. Cours L3 Informatique). Certaines techniques sont à utiliser :
Cependant, les options de compilation des compilateurs permettent parfois d'obtenir un gain substantiel. Par exemple sous gcc/c++ :
Sous Linux, pour connaître les différentes technologies disponibles sur un processeur il suffit de taper la commande suivante :
cat /proc/cpuinfo | grep "model name" | uniq ; cat /proc/cpuinfo | grep -E "flags" | uniq
Voici un exemple de sortie pour un processeur Intel Xeon E5-2670 v2 (Q3'13, 10 cores + 10 threads, 25 Mo L3, Turbo Freq. 3.3 Ghz, \$1550) :
model name : Intel(R) Xeon(R) CPU E5-2670 v2 @ 2.50GHz
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm ida arat xsaveopt pln pts dts tpr_shadow vnmi flexpriority ept vpid fsgsbase smep erms