2010-04-10 10 views
30

Ho sviluppato un'estensione C Python che riceve dati da python e calcola alcuni calcoli intensivi della cpu. È possibile profilare l'estensione C?Estensioni di Python di profilatura C

Il problema qui è che scrivere un test di esempio in C da profilare sarebbe difficile perché il codice si basa su particolari input e strutture di dati (generati dal codice di controllo Python).

Avete qualche suggerimento?

risposta

17

ho trovato la mia strada con google-perftools. Il trucco era di avvolgere le funzioni StartProfiler e StopProfiler in python (attraverso cython nel mio caso).

Per profilare l'estensione C è sufficiente avvolgere il codice Python nelle chiamate StartProfiler e StopProfiler.

from google_perftools_wrapped import StartProfiler, StopProfiler 
impor c_extension # extension to profile c_extension.so 

StartProfiler("output.prof") 
... calling the interesting functions from the C extension module ... 
StopProfiler() 

Poi per analizzare, ad esempio è possibile esportare in formato callgrind e vedere il risultato in kcachegrind:

pprof --callgrind c_extension.so output.prof > output.callgrind 
kcachegrind output.callgrind 
+0

Grazie mille per questo suggerimento !! In realtà stavo cercando la stessa cosa. Cercherò. – ThR37

+0

EDIT: Funziona perfettamente! Un semplice wrapper con ctypes è OK anche se ottengo a volte segfaults durante il profiling della CPU (ma questo è "normale" e spiegato nel doc ... Sto usando x86_64 :() – ThR37

+0

Grazie mille per questo piccolo nugget. molto, molto utile :-) Quello che sto vedendo è che pprof (o meglio, google-pprof nel pacchetto per Debian), è che non ottengo tutti i simboli sbrecciati come farei quando si profila lo stesso codice con valgrind. Potrebbe essere che ho bisogno di specificare -pg durante la compilazione? – miquelramirez

4

Con gprof, è possibile profilo qualsiasi programma che era properly compiled e collegato (gcc -pg ecc, nel caso in cui gprof s'). Se stai usando una versione Python non costruito con gcc (ad esempio, la versione precompilata di Windows il PSF distribuisce), avrete bisogno di ricerca ciò che esistono strumenti equivalenti per quella piattaforma e toolchain (nel caso di Windows PSF, forse mingw può aiutare). Potrebbero esserci dati "irrilevanti" (funzioni C interne nel runtime di Python) e, in tal caso, le percentuali mostrate da gprof potrebbero non essere applicabili, ma i numeri assoluti (di chiamate e di durata) sono ancora validi, ed è possibile post-processare l'output di gprof (ad es. con un piccolo script Python ;-) per escludere i dati irrilevanti e calcolare le percentuali desiderate.

+1

Ho ancora qualche problema nell'utilizzarlo ma forse è solo colpa mia. Dopo aver compilato e collegato (gcc) l'origine python, l'eseguibile produce correttamente il file gmon.out. Se eseguo gli script che caricano le estensioni C (* .so) compilate con flag -pg, l'output di profiling (gprof/path/custom/python, né l'estensione gprof.quindi) non mostra le chiamate di funzione contenute nella libreria C. Mi manca qualcosa? – pygabriel

+2

gprof non funziona bene con dlopen(), presumibilmente perché inizializza le sue mappe di memoria prima che la libreria venga caricata. Semplicemente la compilazione con il flag -pg non fa nulla per aiutare gprof se l'host eseguibile (in questo caso, python) non è collegato direttamente al file .so. –

22

Dopo il commento di pygabriel ho deciso di caricare un pacchetto per PyPI che implementa un profiler per estensioni python utilizzando il cpu-profiler di google-perftools: http://pypi.python.org/pypi/yep

+0

Grazie, ho trovato questo molto utile e abbastanza facile da usare! – robince

+0

Grazie Fabian, speriamo che questo faccia il trucco! –

+0

Ti piace l'utilizzo della memoria del profilo? –

0

Uno dei miei colleghi mi ha detto ltrace(1). Mi ha aiutato molto nella stessa situazione.

assuma il nome dell'oggetto condiviso del C estensione è myext.so e si desidera eseguire benchmark.py, quindi

ltrace -x @myext.so -c python benchmark.py 

La sua uscita è come è necessario

% time  seconds usecs/call  calls  function 
------ ----------- ----------- --------- -------------------- 
24.88 30.202126  7550531   4 ldap_result 
12.46 15.117625  7558812   2 l_ldap_result4 
12.41 15.059652  5019884   3 ldap_chase_v3referrals 
12.41 15.057678  3764419   4 ldap_new_connection 
12.40 15.050310  3762577   4 ldap_int_open_connection 
12.39 15.042360  3008472   5 ldap_send_server_request 
12.38 15.029055  3757263   4 ldap_connect_to_host 
    0.05 0.057890  28945   2 ldap_get_option 
    0.04 0.052182  26091   2 ldap_sasl_bind 
    0.03 0.030760  30760   1 l_ldap_get_option 
    0.03 0.030635  30635   1 LDAP_get_option 
    0.02 0.029960  14980   2 ldap_initialize 
    0.02 0.027988  27988   1 ldap_int_initialize 
    0.02 0.026722  26722   1 l_ldap_simple_bind 
    0.02 0.026386  13193   2 ldap_send_initial_request 
    0.02 0.025810  12905   2 ldap_int_select 
.... 

Particolare attenzione se l'oggetto condiviso ha - o + nel nome del file. Questi caratteri non sono trattati come sono (vedi man ltrace(1) per i dettagli).

Il jolly * può essere una soluzione alternativa come -x @myext* al posto di -x @myext-2.so.

Problemi correlati