# 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

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

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.»
(PLDI)
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.
* 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. 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 o vari carichi di lavoro. 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. 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.

## Storia

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, 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.
Nel 1994, Amitabh Srivastava e Alan Eustace della Digital Equipment Corporation hanno pubblicato un documento che descrive ATOM (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.

## Tipi di profiler basati sull'output
### Flat profiler

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

I profiler basati su grafo 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

I profiler sensibili all'input 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

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.

### Profiler basati su eventi

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

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."
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 sono SmartBear Software AQtime e CLR Profiler di Microsoft. Questi profili supportano anche la profilazione del codice nativo, insieme a Shark (OSX) di Apple Inc., OProfile (Linux), Intel VTune e Parallel Amplifier (parte di Intel Parallel Studio) e Oracle Performance Analyzer, tra gli altri.

### Strumentazione

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. 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

* 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

* 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.