Operation | Time (ns) | Notes |
---|---|---|
L1 cache reference | 0.5 ns | |
Branch mispredict | 5 ns | |
L2 cache reference | 7 ns | 14x L1 cache |
Mutex lock/unlock | 25 ns | |
Main memory reference | 100 ns | 20x L2 cache, 200x L1 cache |
Compress 1K bytes with Zippy | 3,000 ns | |
Send 1K bytes over 1 Gbps network | 10,000 ns | 0.01 ms |
Read 4K randomly from SSD* | 150,000 ns | 0.15 ms |
Read 1 MB sequentially from memory | 250,000 ns | 0.25 ms |
Round trip within same datacenter | 500,000 ns | 0.5 ms |
Read 1 MB sequentially from SSD* | 1,000,000 ns | 1 ms, 4X memory |
Disk seek | 10,000,000 ns | 10 ms, 20x datacenter roundtrip |
Read 1 MB sequentially from disk | 20,000,000 ns | 20 ms 80x memory, 20X SSD |
Send packet Caracal - NY - Caracal | 150,000,000 ns | 150 ms |
Credits:
Un profiler este un utilitar de analiză a performanței care ajută programatorul să determine punctele critice – bottleneck – ale unui program. Acest lucru se realizează prin investigarea comportamentului programului, evaluarea consumului de memorie și relația dintre modulele acestuia.
Profiler-ele bazate pe această tehnică necesită de obicei modificări în codul programului: se inserează secțiuni de cod la începutul și sfârșitul funcției ce se dorește analizată. De asemenea, se rețin și funcțiile apelate. Astfel, se poate estima timpul total al apelului în sine cât și al apelurilor de subfuncții. Dezavantajul major al acestor profilere este legat de modificarea codului: în funcții de dimensiune scăzută și des apelate, acest overhead poate duce la o interpretare greșită e rezultatelor.
Profiler-ele bazate pe sampling nu fac schimbări în codul programului, ci verifică periodic procesorul cu scopul de a determina ce funcție (instrucțiune) se execută la momentul respectiv. Apoi estimează frecvența și timpul de execuție al unei anumite funcții într-o perioada de timp.
Suportul pentru profilere este disponibil la nivel de:
În continuare sunt prezentate câteva unelte folosite în profiling.
gprof
este utilitarul de instrumentare folosit în combinație cu GCC
. Vom lua ca exemplu următoarea secvență de cod:
void a_foo() { printf("a_foo\n"); usleep(2100); } void b_foo() { printf("b_foo\n"); usleep(4200); } int main(void) { int i; for (i = 0; i < 1000; i++) { a_foo(); b_foo(); a_foo(); } return 0; }
Pentru folosirea gprof
trebuie compilat programul cu suport de profiling prin folosirea opțiunii -pg
:
gcc -Wall -g -pg main.c -o main
./main
$ ls
gmon.out main main.c
Avem un fișier gmon.out
care conține datele analizei. Acestea pot fi afișate în mai multe forme:
Folosim acum gprof
pentru a afișa flat profile și call graph pentru programul main.c
.
$ gprof ./main Flat profile: [...] time seconds seconds calls us/call us/call name 50.33 0.01 0.01 2000 2.52 2.52 a_foo 50.33 0.01 0.01 1000 5.03 5.03 b_foo [...] Call graph index % time self children called name <spontaneous> [1] 100.0 0.00 0.01 main [1] 0.01 0.00 2000/2000 a_foo [2] 0.01 0.00 1000/1000 b_foo [3] ----------------------------------------------- 0.01 0.00 2000/2000 main [1] [2] 50.0 0.01 0.00 2000 a_foo [2] ----------------------------------------------- 0.01 0.00 1000/1000 main [1] [3] 50.0 0.01 0.00 1000 b_foo [3] -----------------------------------------------
Observați că:
usleep
.a_foo
este egal cu cel al funcției b_foo
.
Majoritatea procesoarelor moderne oferă registre speciale (performance counters
) care contorizează diferite tipuri de evenimente hardware: instrucțiuni executate, cache-miss-uri, instrucțiuni de salt anticipate greșit, fără să afecteze performanța nucleului sau a aplicațiilor. Aceste registre pot declanșa întreruperi atunci când se acumulează un anumit număr de evenimente și astfel se pot folosi pentru analiza codului care rulează pe procesorul în cauză.
Subsistemul perfcounters
:
CONFIG_PERF_COUNTERS=y
)oprofile
evenimente hardware
(instrucțiuni, accese cache, ciclii de magistrală).evenimente software
(page fault, cpu-clock, cpu migrations).tracepoints
(e.g: sys_enter_open, sys_exit_open).
Utilitarul perf
este interfața subsistemului perfcounters
cu utilizatorul. Oferă o linie de comandă asemănătoare cu git
și nu necesită existența unui daemon.
Un tutorial despre perf - tutorial perf.
$ perf [--version] [--help] COMMAND [ARGS]
Cele mai folosite comenzi
sunt:
annotate
- Citește perf.data
și afișează codul cu adnotărilist
- Listează numele simbolice ale tuturor tipurilor de evenimente ce pot fi urmărite de perf
lock
- Analizează evenimentele de tip lockrecord
- Rulează o comandă și salvează informațiile de profiling în fișierul perf.data
report
- Citește perf.data
(creat de perf record
) și afișează profilulsched
- Utilitar pentru măsurarea proprietăților planificatorului (latențe)stat
- Rulează o comandă și afișează statisticile înregistrate de subsistemul performance counterstop
- Generează și afișează informații în timp real despre încărcarea unui sistem
Afișează numele simbolice ale tuturor tipurilor de evenimente ce pot fi urmărite de perf
.
$ perf list List of pre-defined events (to be used in -e): cpu-cycles OR cycles [Hardware event] instructions [Hardware event] cpu-clock [Software event] page-faults OR faults [Software event] L1-dcache-loads [Hardware cache event] L1-dcache-load-misses [Hardware cache event] rNNN [Raw hardware event descriptor] mem:<addr>[:access] [Hardware breakpoint] syscalls:sys_enter_accept [Tracepoint event] syscalls:sys_exit_accept [Tracepoint event]
Atunci când un eveniment nu este disponibil în forma simbolică, poate fi folosit cu perf în forma procesorului din sistemul analizat.
Rulează o comandă și afișează statisticile înregistrate de subsistemul performance counters.
$ perf stat ls -R /usr/src/linux Performance counter stats for 'ls -R /usr/src/linux': 934.512846 task-clock-msecs # 0.114 CPUs 1695 context-switches # 0.002 M/sec 163 CPU-migrations # 0.000 M/sec 306 page-faults # 0.000 M/sec 725144010 cycles # 775.959 M/sec 419392509 instructions # 0.578 IPC 80242637 branches # 85.866 M/sec 5680112 branch-misses # 7.079 % 174667968 cache-references # 186.908 M/sec 4178882 cache-misses # 4.472 M/sec 8.199187316 seconds time elapsed
perf stat
oferă posibilitatea colectării datelor în urma rulării de mai multe ori a unui program specificând opțiunea -r
.
$ perf stat -r 6 sleep 1 Performance counter stats for 'sleep 1' (6 runs): 1.757147 task-clock-msecs # 0.002 CPUs ( +- 3.000% ) 1 context-switches # 0.001 M/sec ( +- 14.286% ) 0 CPU-migrations # 0.000 M/sec ( +- 100.000% ) 144 page-faults # 0.082 M/sec ( +- 0.147% ) 1373254 cycles # 781.525 M/sec ( +- 2.856% ) 588831 instructions # 0.429 IPC ( +- 0.667% ) 106846 branches # 60.806 M/sec ( +- 0.324% ) 11312 branch-misses # 10.587 % ( +- 0.851% ) 1.002619407 seconds time elapsed ( +- 0.012% )
Observați mai sus evenimentele cele mai importante contorizate.
Generează și afișează informații în timp real despre încărcarea unui sistem.
$ ls -R /home $ perf top -p $(pidof ls) -------------------------------------------------------------- PerfTop: 181 irqs/sec kernel:72.4% (target_pid: 10421) -------------------------------------------------------------- samples pcnt function DSO _______ _____ ____________________ ___________________ 270.00 15.8% __d_lookup [kernel.kallsyms] 145.00 8.5% __GI___strcoll_l /lib/libc-2.12.1.so 99.00 5.8% link_path_walk [kernel.kallsyms] 97.00 5.7% find_inode_fast [kernel.kallsyms] 91.00 5.3% __GI_strncmp /lib/libc-2.12.1.so 55.00 3.2% move_freepages_block [kernel.kallsyms] 44.00 2.6% ext3_dx_find_entry [kernel.kallsyms] 41.00 2.4% ext3_find_entry [kernel.kallsyms] 40.00 2.3% dput [kernel.kallsyms] 39.00 2.3% ext3_check_dir_entry [kernel.kallsyms]
Observăm că funcțiile de lucru cu fișiere (parcurgere, căutare) sunt cele care apar cel mai des în outputul lui perf-top corespunzător rulării comenzii de listare recursivă a directorului home
.
Rulează o comandă și salvează informațiile de profiling în fișierul perf.data
.
$ perf record wget http://elf.cs.pub.ro/so/wiki/laboratoare/laborator-12 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.008 MB perf.data (~334 samples) ] $ ls laborator-12 perf.data
Interpretează datele salvate în perf.data
în urma analizei folosind perf record
. Astfel pentru exemplul wget
de mai sus avem:
$ perf report # Events: 13 cycles # # Overhead Command Shared Object Symbol # ........ ....... ................. ...... # 86.43% wget e8ee21 [.] 0x00000000e8ee21 11.03% wget [kernel.kallsyms] [k] prep_new_page 2.37% wget [kernel.kallsyms] [k] sock_aio_read 0.11% wget [kernel.kallsyms] [k] perf_event_comm 0.05% wget [kernel.kallsyms] [k] native_write_msr_safe
01-bubble-sort
.bubble-sort.c
.bubble_sort
. Aceasta va realiza:swap
pentru a interschimba 2 elemente.gprof
, rezultatele obținute în momentul utilizării funcției bubble_sort
implementate în C și funcției bubble_sort
implementate în limbaj de asamblare:TYPE
cu valoarea ASSEMBLY__
și salvați rezultatele în gmon-as.out
. $gprof ./bubble-sort > gmon-as.out
TYPE
cu valoarea C__
și salvați rezultatele în gmon-c.out
.-O2
pentru a compila sursa folosind suportul de optimizări ale compilatorului și comparați rezultatele de profiling obținute cu cele anterioare.CFLAGS
din Makefile
.gmon-as-O2.out
respectiv gmon-c-02.out
.-b
cu gprof
pentru a evita încărcarea output-ului cu explicații.02-major
.perf_3.2
determinați dacă limbajul C este column-major sau row-major (row-major-order). row.c
astfel încât să incrementeze elementele unei matrice pe linii.columns.c
astfel încât să incrementeze elementele unei matrice pe coloane.perf stat
pentru a urmări evenimentele L1-dcache-loads
și L1-dcache-load-misses
.-e
a utilitarului perf
pentru a specifica un anumit eveniment de urmărit.03-busy
busy.c
.busy
și analizați încărcarea sistemului folosind comanda perf top
.04-hash/
.04-hash/
se găsește o implementare a unui algoritm pentru tabele de dispersie. gprof
pentru a îmbunătăți comportarea programului.05-find-char/
.find-char.c
.find-char.c
și rulați executabilul obținut.gprof
, care este funcția care ocupă cel mai mult timp de procesor și încercați să îmbunătățiți performanțele programului.06-tlb/
.tlb-processes.c
, respectiv tlb-threads.c
. Compilați cele două programe.tlb-processes.c
- folosește 2 procese ce accesează o zonă de memorie partajată.tlb-threads.c
- folosește 2 fire de execuție ce accesează o zonă de memorie partajată.time
. perf stat
pentru a determina acest lucru.tlb-misses
?perf list
pentru a determina evenimentul corespunzător cu data tlb misses
.