## Manuale Utente: Python Profile Analyzer (Italiano) ### 1. Introduzione Benvenuto in Python Profile Analyzer! Questo strumento è progettato per rispondere a una delle domande più comuni nello sviluppo software: "Perché il mio codice è lento?". Python Profile Analyzer ti permette di eseguire il tuo codice sotto il profiler standard di Python, `cProfile`, e di analizzare i risultati attraverso un'interfaccia grafica intuitiva. Invece di leggere file di testo complessi, potrai navigare tra i dati, ordinare, filtrare e visualizzare le interazioni tra le funzioni per identificare rapidamente i colli di bottiglia e ottimizzare le performance della tua applicazione. ### 2. Panoramica dell'Interfaccia L'interfaccia è divisa in tre aree principali: 1. **Barra degli Strumenti Superiore**: * **Load Profile File...**: Carica un file di dati di profiling (`.prof`) già esistente. * **Profile a Script...**: Apre il Gestore Profili per configurare ed eseguire il profiling di un nuovo script o modulo. * **Export to CSV...**: Esporta i dati attualmente visualizzati nella tabella in un file CSV. * **Current Profile**: Mostra il nome del file di profilo attualmente caricato. 2. **Pannello delle Statistiche Principali**: * È un'area a schede (tab) che fornisce diverse viste sui dati: * **Table View**: La vista principale. Una tabella interattiva con le statistiche di ogni funzione. * **Text View**: L'output testuale standard di `pstats`, utile per una visione d'insieme. * **Graph View**: (Disponibile solo se Graphviz è installato) Una visualizzazione a grafo delle chiamate. * **Controlli (sotto le schede)**: Permettono di ordinare e filtrare i dati visualizzati nella tabella e nella vista testuale. 3. **Pannello dei Dettagli (in basso)**: * Quando selezioni una funzione nella **Table View**, questi due pannelli si popolano automaticamente: * **Callers**: Mostra quali funzioni hanno chiamato la funzione selezionata. * **Callees**: Mostra quali funzioni sono state chiamate dalla funzione selezionata. ### 3. Flussi di Lavoro Principali #### A. Eseguire il Profiling di un Nuovo Script Questo è il modo più comune per iniziare. 1. Clicca su **"Profile a Script..."**. Si aprirà la finestra "Launch Profile Manager". 2. Clicca su **"New"** per creare un nuovo profilo. 3. **Compila i dettagli del profilo**: * **Profile Name**: Un nome descrittivo (es. "Mio Script di Test"). * **Run as Module**: Seleziona questa opzione se vuoi lanciare un modulo (es. `python -m mio_modulo`). * **Script Path** (se non è un modulo): Clicca `...` per selezionare il file `.py` da eseguire. * **Project Root Folder** (se è un modulo): Clicca `...` per selezionare la cartella radice del tuo progetto. * **Module Name** (se è un modulo): Inserisci il nome del modulo da lanciare (es. `mio_modulo.main`). * **Arguments**: Inserisci eventuali argomenti da passare allo script (es. `--input file.txt --verbose`). 4. Clicca su **"Save Changes"**. 5. Il tuo nuovo profilo apparirà nella lista a sinistra. Selezionalo e clicca **"Run & Profile Selected"**. 6. L'applicazione eseguirà lo script in background. Al termine, i risultati verranno automaticamente caricati e visualizzati. #### B. Caricare un File di Profilo Esistente Se hai già un file di dati (`.prof`) generato in precedenza (anche da `cProfile` da riga di comando), puoi analizzarlo. 1. Clicca su **"Load Profile File..."**. 2. Seleziona il tuo file `.prof` dalla finestra di dialogo. 3. I dati verranno caricati e visualizzati nell'interfaccia. ### 4. Analisi dei Risultati: Un Caso d'Uso Comprendere i dati è la chiave. Immaginiamo di avere il seguente script `slow_script.py` da analizzare: ```python # slow_script.py import time def utility_function(n): # Funzione veloce, ma chiamata molte volte return n * 2 def expensive_calculation(): # Funzione intrinsecamente lenta print("Doing expensive calculation...") time.sleep(2) # Simula un calcolo pesante print("...done.") def process_data(): # Funzione che orchestra altre chiamate print("Processing data...") total = 0 for i in range(1000): total += utility_function(i) expensive_calculation() print(f"Final total: {total}") if __name__ == "__main__": process_data() ``` Dopo aver eseguito il profiling di questo script, ecco come interpretare i risultati. #### Passo 1: Qual è la funzione più lenta in assoluto? Vai alla **Table View** e clicca sull'header della colonna **"Total Time (s)"** per ordinare in modo decrescente. * **Cosa cercare**: La funzione in cima alla lista. `Total Time` (o `tottime`) è il tempo speso *all'interno* di una funzione, escludendo il tempo speso nelle funzioni che essa chiama. * **Risultato Atteso**: Vedrai `time.sleep` o una funzione simile in cima, con un `tottime` di circa 2 secondi. Subito dopo, vedrai `expensive_calculation`, ma con un `tottime` molto basso, perché quasi tutto il suo tempo è stato speso in `time.sleep`. Questo ti dice che `expensive_calculation` è lenta a causa di una specifica operazione al suo interno. #### Passo 2: Quale funzione è un collo di bottiglia a causa delle funzioni che chiama? Ora, ordina per **"Cum. Time (s)"** (Tempo Cumulativo). * **Cosa cercare**: `Cumulative Time` (o `cumtime`) è il tempo totale speso in una funzione, *incluso* il tempo speso in tutte le sotto-chiamate. È utile per trovare le funzioni "manager" che sono lente a causa del lavoro che delegano. * **Risultato Atteso**: `process_data` sarà in cima alla lista con un `cumtime` di poco superiore a 2 secondi. Il suo `tottime` sarà basso, ma il `cumtime` alto ci dice che è il punto di partenza del nostro collo di bottiglia. #### Passo 3: C'è una funzione che viene chiamata troppe volte? Ordina per **"N-Calls"** (Numero di Chiamate). * **Cosa cercare**: Funzioni con un numero di chiamate eccezionalmente alto. Anche una funzione veloce, se chiamata milioni di volte, può causare un rallentamento a causa dell'overhead. * **Risultato Atteso**: `utility_function` apparirà in cima con 1000 chiamate. Selezionandola, vedrai che il suo `tottime` e `cumtime` sono molto bassi, quindi in questo caso non è un problema. Ma se lo fossero, avresti trovato un candidato per l'ottimizzazione (es. vettorizzazione). #### Passo 4: Come posso visualizzare il flusso di esecuzione? Passa alla scheda **Graph View**. * **Cosa cercare**: Il "percorso caldo" (hot path). I nodi (funzioni) sono colorati da verde (veloce) a rosso (lento) in base al loro `tottime`. Le frecce indicano il flusso delle chiamate. * **Risultato Atteso**: Vedrai un percorso che va da `process_data` a `expensive_calculation`, e da lì a `time.sleep`. Il nodo `time.sleep` sarà rosso vivo, indicando chiaramente che è la fonte del rallentamento. Vedrai anche una freccia da `process_data` a `utility_function` con l'etichetta "1000 calls". * **Consiglio**: Usa lo slider **"Node Threshold"** per filtrare le funzioni poco significative e ridurre il rumore nei grafi complessi. #### Passo 5: Chi chiama questa funzione lenta e cosa chiama? Torna alla **Table View** e seleziona la riga di `expensive_calculation`. * **Guarda il pannello Callers**: Vedrai che è stata chiamata da `process_data`. * **Guarda il pannello Callees**: Vedrai che ha chiamato `time.sleep`. Questo conferma il contesto della chiamata, aiutandoti a capire dove e perché una funzione viene eseguita. ### 5. Risoluzione dei Problemi * **La "Graph View" è disabilitata o grigia**: 1. Assicurati di aver installato il software **Graphviz** (non solo la libreria Python). 2. Assicurati che la cartella `bin` di Graphviz sia nel `PATH` del tuo sistema. 3. Assicurati di aver installato la libreria `Pillow` (`pip install Pillow`). 4. Dopo aver caricato un profilo, un grafo iniziale viene generato automaticamente. Se non vedi nulla, prova a cliccare "Generate Graph" o a ridurre la soglia.