2013-08-12 12 views
5

Sto cercando di ottimizzare le prestazioni di un programma C++ e ridurre il tempo di esecuzione. Tuttavia, ho difficoltà a capire dove si trova il collo di bottiglia.Come analizzare il tempo di esecuzione del programma

Il comando tempo indica che il programma stesso richiede circa 5 minuti per l'esecuzione, e circa 5 minuti, il tempo di CPU dell'utente richiede 4,5 minuti.

Il profilo della CPU (sia gcc profiler che google perftool) mostra che le chiamate di funzione impiegano solo 60 secondi in totale nel tempo della CPU. Ho anche provato ad usare il profiler per campionare in tempo reale invece del tempo della cpu, e mi dà risultati simili.

I/O profiler (io ho usato ioapps) mostra anche che I/O richiede solo circa 30 secondi del tempo di esecuzione del programma.

Quindi in pratica ho 3,5 minuti (la maggior parte del tempo di esecuzione del programma) non contati, e credo che sia dove si trova il collo di bottiglia.

Cosa mi sono perso e come faccio a sapere dove va quel tempo?

+0

è il codice multithread? Inizia un altro processo (ad esempio 'fork', con o senza 'exec')? –

+0

Hai provato il built-in perf di Linux? 'perf record ' quindi 'perf report'. – DanielKO

+1

Con quello che ci hai dato, la mia ipotesi migliore è che tu non stia leggendo correttamente l'output del profiler. Pubblica l'output di gprof/perf/strace (almeno le prime 15 righe o giù di lì). – DanielKO

risposta

6

Come suggerito da Öö Tiib, è sufficiente interrompere il programma in un debugger. Il modo in cui lo faccio è far funzionare il programma, passare alla finestra di output, digitare Ctrl-C per interrompere il programma, tornare alla finestra GDB, digitare "thread 1" in modo da essere nel contesto del programma principale, e digitare "bt" per vedere la traccia dello stack.

Ora, guarda la traccia dello stack e la capisci, perché mentre l'istruzione al contatore del programma è responsabile per quel particolare ciclo che viene speso, così è ogni chiamata nello stack.

Se si esegue questa operazione alcune volte, si vedrà esattamente quale linea è responsabile del collo di bottiglia. Non appena lo vedi su due (2) campioni, lo hai inchiodato. Quindi aggiustalo e fai di nuovo tutto, trova il collo di bottiglia successivo e così via. Si potrebbe facilmente scoprire che si ottiene enorme accelerazione in questo modo.

< fiamma>

Alcune persone dicono che questo è esattamente ciò che fanno i profiler, solo lo fanno meglio. Ecco cosa si sente nelle aule e nei blog, ma ecco l'affare: Ci sono modi per accelerare il codice che non si rivelano come "funzioni lente" o "percorsi caldi", ad esempio - riorganizzare la struttura dati. Ogni funzione sembra più o meno innocente, anche se ha un'elevata percentuale di tempo.

Si rivelano da soli se si visualizza effettivamente campioni pila. Quindi il problema con i buoni profiler non è nella collezione di campioni, è nella presentazione dei risultati. Le statistiche e le misurazioni non possono dirti quale piccola selezione di campioni, esaminata attentamente, ti dico.

E il numero di campioni piccoli o grandi? Non sono meglio? OK, supponiamo che tu abbia un ciclo infinito, o se non infinito, funzioni solo molto più a lungo di quanto tu possa immaginare? 1000 campioni di stack lo troverebbero meglio di un singolo campione? (No.) Se lo guardi con un debugger, sai che sei nel ciclo perché ci vuole praticamente il 100% delle volte. È in pila da qualche parte - basta esaminare lo stack finché non lo trovi. Anche se il ciclo prende solo il 50% o il 20% delle volte, questa è la probabilità che ogni campione la vedrà. Quindi, se vedi qualcosa di cui potresti sbarazzarti con solo due campioni, vale la pena farlo. Allora, cosa ti comprano i 1000 campioni?

Forse si pensa: "E se ci manca un problema o due? Forse è abbastanza buono." Bene, vero? Supponiamo che il codice abbia tre problemi: P sta prendendo il 50% delle volte, Q sta prendendo il 25% e R sta prendendo il 12,5%. La roba buona si chiama A. Questo mostra l'accelerazione che si ottiene se si aggiusta uno di loro, due di loro, o tutti e tre di loro.

PRPQPQPAPQPAPRPQ original time with avoidable code P, Q, and R all mixed together 
RQQAQARQ   fix P   - 2 x speedup 
PRPPPAPPAPRP  fix Q   - 1.3 x " 
PPQPQPAPQPAPPQ fix R   - 1.14 x " 
RAAR    fix P and Q  - 4 x  " 
QQAQAQ   fix P and R  - 2.7 x " 
PPPPAPPAPP  fix Q and R  - 1.6 x " 
AA    fix P, Q, and R - 8 x speedup 

Questo chiarisce perché quelli che "scappano" fanno davvero male? Il migliore si può fare se si perde qualcuno è due volte più lento.

Sono facili da trovare se si esaminano i campioni. P è sulla metà dei campioni. Se ripari P e lo fai di nuovo, Q è a metà dei campioni. Una volta sistemato Q, R è sulla metà dei campioni. Fix R e hai il tuo 8x di accelerazione. Non devi fermarti qui. Puoi andare avanti finché non trovi davvero nulla da sistemare.

Più problemi ci sono, maggiore è la velocità potenziale, ma non puoi permetterti di perdere nessuno. Il problema con i profiler (anche quelli buoni) è che, negandoti la possibilità di vedere e studiare i singoli campioni, nascondono i problemi che devi trovare. More on all that. For the statistically inclined, here's how it works.

ci sono buone profiler. I migliori sono campionatori di stack wall-time che riportano percentuali inclusive su singole linee, consentendo di attivare e disattivare il campionamento con un tasto di scelta rapida. Zoom (wiki) è un tale profiler.

Ma anche quelli fanno l'errore di assumere che hai bisogno di molti campioni. Non lo fai, e il prezzo che paghi per loro è che non puoi vederne nessuno, quindi non puoi vedere perché il tempo impiegato è, quindi non puoi facilmente capire se è necessario, e non puoi liberarti di qualcosa se non sai che non ne hai bisogno. Il risultato è che ti mancano i colli di bottiglia e finiscono per perdere la velocità.

</fiamma>

Problemi correlati