Profilazione (programmazione)

Nell'ingegneria del software, la profilazione ("profiling del programma", "profiling del software") è una forma di analisi dinamica del programma che misura, ad esempio, lo spazio (memoria) o la complessità temporale di un programma, l'uso di istruzioni particolari o la frequenza e la durata delle chiamate delle funzioni. Più comunemente, le informazioni derivanti dalla profilazione servono a supportare l'ottimizzazione di un software, indicando le parti più lente o pesanti su cui conviene lavorare.

La profilazione si ottiene modificando (strumentazione) il codice sorgente del programma o la sua forma eseguibile binaria usando uno strumento chiamato profiler (o profiler del codice) per poter rilevare il suo stato interno. I profilatori possono utilizzare una serie di tecniche diverse, come metodi basati su eventi, statistici, strumentati e di simulazione.

Raccolta degli eventi del programma modifica

I profilatori utilizzano una vasta gamma di tecniche per raccogliere dati, inclusi interrupt di processo, strumentazione del codice, simulazione delle istruzioni, hook del sistema operativo e contatori delle prestazioni. I profili risultanti sono utilizzati nel processo di ingegneria delle prestazioni per permettere allo sviluppatore e agli strumenti predisposti di effettuare modifiche mirate.

Uso dei profiler modifica

 
Output grafico del profiler di CodeAnalyst
(EN)

«Program analysis tools are extremely important for understanding program behavior. Computer architects need such tools to evaluate how well programs will perform on new architectures. Software writers need tools to analyze their programs and identify critical sections of code. Compiler writers often use such tools to find out how well their instruction scheduling or branch prediction algorithm is performing...»

(IT)

«Gli strumenti di analisi dei programmi sono estremamente importanti per capire il comportamento dei programmi. I costruttori di computer utilizzano strumenti del genere per determinare le prestazioni dei programmi sulle nuove architetture.

Gli autori di software hanno bisogno di strumenti per analizzare i loro programmi e identificare sezioni critiche del codice. Gli autori dei compilatori spesso usano strumenti di questo tipo per determinare le prestazioni del codice da loro generato.»

L'output di un profiler può essere:

  • Un riepilogo statistico degli eventi osservati (un profilo)
Le informazioni di riepilogo del profilo vengono spesso visualizzate come annotazioni rispetto alle istruzioni del codice sorgente in cui si verificano gli eventi, quindi la dimensione dei dati prodotti dalla misurazione è proporzionale alla dimensione del codice del programma.
 / * ------------ sorgente -------------------
conteggio
0001             IF X = "A"                     0055
0002                THEN DO
0003                  ADD 1 to XCOUNT           0032
0004                ELSE
0005             IF X = "B"                     0055
  • Un flusso di eventi registrati (una traccia)
Per i programmi sequenziali, di solito è sufficiente un profilo di riepilogo, ma i problemi di prestazioni nei programmi paralleli (l'attesa di messaggi o i problemi di sincronizzazione) dipendono spesso dalla relazione temporale degli eventi, richiedendo quindi una traccia completa delle diverse componenti per comprendere cosa sta succedendo e come queste interagiscono tra loro.
La dimensione di una traccia (completa) è proporzionale rispetto alla lunghezza del percorso di istruzione del programma, rendendola poco pratica. Una traccia può quindi essere avviata in un punto di un programma e terminata in un altro punto per limitare l'output.
  • Un'interazione con l'hypervisor (monitoraggio continuo o periodico tramite visualizzazione sullo schermo, ad esempio)
Questo offre l'opportunità di attivare o disattivare manualmente una traccia in qualsiasi punto desiderato durante l'esecuzione, oltre a visualizzare le metriche in corso sul programma (ancora in esecuzione). Offre inoltre l'opportunità di sospendere i processi asincroni in punti critici per esaminare le interazioni con altri processi paralleli in modo più dettagliato.

Un profiler può essere applicato a un singolo metodo o a un intero modulo o a un programma, per identificare i colli di bottiglia delle prestazioni evidenziando il codice di che consuma più tempo.[1] Un profiler può essere utilizzato per comprendere il codice da un punto di vista temporale, con l'obiettivo di ottimizzarlo per gestire diverse condizioni di runtime[2] o vari carichi di lavoro.[3] I risultati della profilazione possono essere sfruttati da un compilatore che fornisce l'ottimizzazione guidata dal profilo, ossia modifica il codice generato sulla base delle statistiche dell'esecuzione.[4] I risultati della profilazione possono anche essere utilizzati per guidare la progettazione e l'ottimizzazione di un singolo algoritmo.

I profiler sono integrati in alcuni sistemi di gestione delle prestazioni delle applicazioni che aggregano i dati di profilazione per fornire informazioni sui carichi di lavoro delle transazioni nelle applicazioni distribuite, per esempio i database o servizi web.[5]

Storia modifica

Gli strumenti di analisi delle prestazioni erano disponibili già sulle piattaforme IBM / 360 e IBM / 370 dai primi anni '70, generalmente basati su interruzioni del timer che registravano l'istruzione corrente a intervalli di timer impostati per rilevare "punti critici" nell'esecuzione del codice. Questo è stato un primo esempio di campionamento (vedi sotto). All'inizio del 1974 i simulatori di set di istruzioni permettevano un tracciamento completo e altre funzionalità di monitoraggio delle prestazioni.

L'analisi del programma basata su profiler in Unix risale al 1973,[6] quando i sistemi Unix inclusero uno strumento di base, prof, che elencava ciascuna funzione e il tempo consumato per eseguirla. Nel 1982 gprof estese il concetto a un'analisi completa del grafico delle chiamate.[7]

Nel 1994, Amitabh Srivastava e Alan Eustace della Digital Equipment Corporation hanno pubblicato un documento che descrive ATOM[8] (Analysis Tools with OM, Strumenti di analisi con OM). La piattaforma ATOM converte un programma nel proprio profiler: in fase di compilazione, inserisce del codice aggiuntivo nel programma da analizzare. Questo codice aggiuntivo produce dati per la successiva analisi. Questa tecnica, che modifica un programma per analizzare sé stesso, è nota come " strumentazione ".

Nel 2004 i paper originali su gprof e ATOM sono comparsi nell'elenco dei 50 documenti PLDI più influenti per il ventennio nel 1979-1999.[9]

Tipi di profiler basati sull'output modifica

Flat profiler modifica

I flat profiler calcolano i tempi medi di chiamata, a partire dalle informazioni sulle chiamate stesse, e non suddividono i tempi di chiamata in base al chiamante o al contesto.

Profiler basato su grafo delle chiamate modifica

I profiler basati su grafo[7] mostrano i tempi e le frequenze delle chiamate e le catene di chiamate coinvolte in base al chiamante. In alcuni strumenti non viene preservato il contesto completo.

Profiler sensibile agli input modifica

I profiler sensibili all'input[10][11][12] aggiungono un'ulteriore dimensione ai profiler flat o basati sul grafo delle chiamate e mettendo in relazione le misure delle prestazioni con le caratteristiche dei carichi di lavoro di input, come le dimensioni di input o i valori di input. Generano grafici che caratterizzano il modo in cui le prestazioni di un'applicazione variano in funzione del suo input.

Granularità dei dati nei tipi di profiler modifica

I profiler, che sono essi stessi programmi, analizzano i programmi target raccogliendo informazioni sulla loro esecuzione. In base alla granularità dei dati raccolti sono classificati in profiler basati su eventi o statistici. I profiler interrompono l'esecuzione del programma per raccogliere informazioni, il che limita la risoluzione degli eventi misurati tempo (eventi troppo veloci e rari possono essere ignorati), riducendo l'accuratezza. I profilatori a blocchi base riportano le quantità di cicli di clock della macchina dedicati all'esecuzione di ciascuna riga di codice o un tempo basato sulla loro somma; i tempi riportati per blocco di base potrebbero non riflettere una differenza tra utilizzo o meno della cache.[13][14]

Profiler basati su eventi modifica

I linguaggi di programmazione elencati qui hanno profiler basati su eventi:

  • Java: l'API JVMTI (JVM Tools Interface), precedentemente JVMPI (JVM Profiling Interface), fornisce hook ai profiler per intercettare eventi come chiamate, class-load, unload o entrata e uscita dai thread.
  • .NET: È possibile collegare un agente di profilazione come server COM al CLR utilizzando l'API di profilazione. Come Java, il runtime fornisce quindi vari callback nell'agente, per la rilevazione di eventi come l'utilizzo di JIT / enter / leave, la creazione di oggetti, ecc. È particolarmente potente in quanto l'agente di profilazione può riscrivere il bytecode dell'applicazione di destinazione in modi arbitrari.
  • Python: la profilazione Python include il modulo profile, hotshot (basato sui grafi) e l'utilizzo della funzione 'sys.setprofile' per intercettare eventi come c_ {call, return, exception}, python_ {call, return, exception}. Librerie di terze parti forniscono ulteriori dettagli e permettono di esaminare programmi paralleli
  • Ruby: Anche Ruby utilizza un'interfaccia simile a Python per la profilazione. Sono presenti flat-profiler in profile.rb, modulie e l'estensione C ruby-prof.

Profiler statistici modifica

Alcuni profilatori operano usando un campionamento. Un profiler di campionamento analizza lo stack di chiamate del programma target a intervalli regolari utilizzando gli interrupt di sistema operativo. I profili di campionamento sono in genere meno precisi e specifici dal punto di vista numerico, ma consentono al programma target di essere eseguito a velocità paragonabili a quella originale.

I dati risultanti non sono esatti, ma un'approssimazione statistica. "La quantità effettiva di errore è generalmente superiore a un periodo di campionamento. Infatti, se un valore è n volte il periodo di campionamento, l'errore previsto è la radice quadrata di n periodi di campionamento."[15]

In pratica, i profiler basati sul campionamento possono spesso fornire un quadro più accurato dell'esecuzione del programma target rispetto ad altri approcci, in quanto non sono così invasivi rispetto al programma target e quindi non hanno così tanti effetti collaterali (come sulla memoria cache o sulle pipeline di decodifica delle istruzioni). Inoltre, poiché non influiscono molto sulla velocità di esecuzione, possono rilevare problemi che altrimenti rimarrebbero nascosti. Sono anche relativamente immuni dalla sopravvalutazione del costo di piccole routine chiamate spesso o cicli "stretti". Possono mostrare la quantità di tempo trascorso in modalità utente rispetto a quello trascorso in modalità kernel per l'elaborazione delle chiamate di sistema.

Tuttavia, il codice del kernel per gestire gli interrupt comporta una minore perdita di cicli della CPU, altera l'utilizzo della cache e non è in grado di distinguere le varie attività che si verificano nel codice del kernel ininterrotto (attività nell'ordine di microsecondi).

L'hardware dedicato può fare di più oltre: ARM Cortex-M3 e alcuni recenti processori MIPS con interfaccia JTAG hanno un registro chiamato PCSAMPLE, che campiona il contatore del programma in un modo davvero trasparente (poiché fatto da circuiti dedicati che non influenzano l'esecuzione normale) consentendo la raccolta non intrusiva di un profilo piatto.

Alcuni profiler statistici comunemente usati[16] sono SmartBear Software AQtime[17] e CLR Profiler di Microsoft.[18] Questi profili supportano anche la profilazione del codice nativo, insieme a Shark (OSX) di Apple Inc.,[19] OProfile (Linux),[20] Intel VTune e Parallel Amplifier (parte di Intel Parallel Studio) e Oracle Performance Analyzer,[21] tra gli altri.

Strumentazione modifica

Questa tecnica aggiunge istruzioni al programma target per raccogliere le informazioni richieste. Si noti che la strumentazione di un programma può causare cambiamenti nelle prestazioni e in alcuni casi può portare a risultati imprecisi e/o heisenbug. L'effetto dipende dalle informazioni raccolte, dal livello temporale dei dettagli riportati e dall'utilizzo della profilatura dei blocchi di base insieme alla strumentazione.[22] Ad esempio, l'aggiunta di codice per contare ogni procedura/chiamata di routine avrà probabilmente un effetto minore rispetto al conteggio di quante volte viene rispettata ogni singola istruzione. Alcuni computer dispongono di hardware speciale per raccogliere informazioni; in questo caso l'impatto sul programma è minimo.

La strumentazione è essenziale per determinare il livello di controllo e la risoluzione temporale disponibile per i profiler.

  • Manuale: eseguita dal programmatore, ad esempio aggiungendo istruzioni per calcolare esplicitamente i tempi di esecuzione, contare degli eventi o delle chiamate alle API di misurazione come lo standard Application Response Measurement.
  • Automatico sul codice sorgente: strumentazione aggiunta al codice sorgente da uno strumento automatico secondo una politica di strumentazione.
  • Linguaggio intermedio: strumentazione aggiunta al codice assembly o al bytecode che supporta più linguaggi sorgente di livello superiore ed evita problemi di riscrittura dell'offset binario (non simbolico). In sostanza se il programma viene compilato in un formato intermedio la strumentazione avviene su questo formato.
  • Assistita dal compilatore: il compilatore può avere delle funzioni per produrre direttamente un binario che incorpora la strumentazione.
  • Traduzione binaria: lo strumento aggiunge la strumentazione a un eseguibile compilato.
  • Strumentazione di runtime: direttamente prima dell'esecuzione il codice viene strumentato. L'esecuzione del programma è completamente supervisionata e controllata dal profiler.
  • Iniezione di runtime: più leggera della strumentazione di runtime. Il codice viene modificato in fase di esecuzione, eventualmente in un secondo tempo quando è già avviato, per avere salti alle funzioni di supporto alla profilazione.

Strumentazione dell'interprete modifica

  • Le opzioni di debug dell'interprete permettono la raccolta di metriche delle prestazioni quando l'interprete incontra ogni istruzione target. Un interprete bytecode, a tabella di controllo o JIT sono tre esempi che di solito hanno il controllo completo sull'esecuzione del codice target, consentendo così modalità di raccolta dati estremamente complete.

Hypervisor / Simulatore modifica

  • Hypervisor: i dati vengono raccolti eseguendo il programma (generalmente) non modificato in un hypervisor. Esempio: SIMMON
  • Simulatore e hypervisor: dati raccolti in modo interattivo e selettivo eseguendo il programma non modificato in un simulatore di set di istruzioni.

Note modifica

  1. ^ How to find the performance bottleneck in C# desktop application?, su stackoverflow.com, Stack Overflow, 2012.
  2. ^ Kirk J Krauss, Performance Profiling with a Focus, su developforperformance.com, Develop for Performance, 2017. URL consultato il 13 gennaio 2020 (archiviato dall'url originale il 23 agosto 2018).
  3. ^ What is code profiling? Learn the 3 Types of Code Profilers, su Stackify Developer Tips, Tricks and Resources, Disqus, 2016.
  4. ^ Eric Lawrence, Getting Started with Profile Guided Optimization, su testslashplain, WordPress, 2016.
  5. ^ List of .Net Profilers: 3 Different Types and Why You Need All of Them, su Stackify Developer Tips, Tricks and Resources, Disqus, 2016.
  6. ^ Unix Programmer's Manual, 4th Edition
  7. ^ a b S.L. Graham, P.B. Kessler, and M.K. McKusick, gprof: a Call Graph Execution Profiler, Proceedings of the SIGPLAN '82 Symposium on Compiler Construction, SIGPLAN Notices, Vol. 17, No 6, pp. 120-126; doi:10.1145/800230.806987
  8. ^ A. Srivastava and A. Eustace, ATOM: A system for building customized program analysis tools, Proceedings of the ACM SIGPLAN Conference on Programming language design and implementation (PLDI '94), pp. 196-205, 1994; ACM SIGPLAN Notices - Best of PLDI 1979-1999 Homepage archive, Vol. 39, No. 4, pp. 528-539; doi:10.1145/989393.989446
  9. ^ 20 Years of PLDI (1979–1999): A Selection, Kathryn S. McKinley, Editor
  10. ^ E. Coppa, C. Demetrescu, and I. Finocchi, Input-Sensitive Profiling, IEEE Trans. Software Eng. 40(12): 1185-1205 (2014); doi:10.1109/TSE.2014.2339825
  11. ^ D. Zaparanuks and M. Hauswirth, Algorithmic Profiling, Proceedings of the 33rd ACM SIGPLAN Conference on Programming Language Design and Implementation (PLDI 2012), ACM SIGPLAN Notices, Vol. 47, No. 6, pp. 67-76, 2012; doi:10.1145/2254064.2254074
  12. ^ T. Kustner, J. Weidendorfer, and T. Weinzierl, Argument Controlled Profiling, Proceedings of Euro-Par 2009 – Parallel Processing Workshops, Lecture Notes in Computer Science, Vol. 6043, pp. 177-184, 2010; doi:10.1007/978-3-642-14122-5 22
  13. ^ Timing and Profiling - Basic Block Profilers, su OpenStax CNX Archive. URL consultato il 15 gennaio 2020 (archiviato dall'url originale il 28 giugno 2020).
  14. ^ Thomas Ball e James R. Larus, Optimally profiling and tracing programs (PDF), in ACM Transactions on Programming Languages and Systems (TOPLAS), vol. 16, n. 4, ACM Digital Library, 1994, pp. 1319–1360, DOI:10.1145/183432.183527. URL consultato il 15 gennaio 2020 (archiviato dall'url originale il 18 maggio 2018).
  15. ^ Statistical Inaccuracy of gprof Output Archiviato il 29 maggio 2012 in Internet Archive.
  16. ^ Popular C# Profilers, su ginktage.com, Gingtage, 2014.
  17. ^ Sampling Profiler - Overview, su AQTime 8 Reference, SmartBear Software, 2018.
  18. ^ Maira Wenzal, Profiling Overview, su Microsoft .NET Framework Unmanaged API Reference, Microsoft, 2017.
  19. ^ Performance Tools, su Apple Developer Tools, Apple, Inc., 2013.
  20. ^ Zanella Netto, Evaluate performance for Linux on Power, su IBM DeveloperWorks, 2012.
  21. ^ 2013, https://books.google.com/books?id=-I64BAAAQBAJ&pg=PA27&lpg=PA27.
  22. ^ Gary Carleton, Knud Kirkegaard e David Sehr, Profile-Guided Optimizations, Dr. Dobb's Journal, 1998.

Voci correlate modifica

Collegamenti esterni modifica