2010-04-10 23 views
30

He desarrollado una extensión C python que recibe datos de python y calcula algunos cálculos intensivos de CPU. ¿Es posible perfilar la extensión C?Perfilando extensiones python C

El problema aquí es que escribir una prueba de muestra en C para ser perfilada sería un reto porque el código se basa en entradas particulares y estructuras de datos (generadas por el código de control de python).

¿Tiene alguna sugerencia?

Respuesta

17

He encontrado mi camino usando google-perftools. El truco fue envolver las funciones StartProfiler y StopProfiler en python (a través de cython en mi caso).

Para perfilar la extensión C es suficiente para ajustar el código python dentro de las llamadas StartProfiler y 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() 

Luego de analizar, por ejemplo, se puede exportar en formato Callgrind y ver el resultado en kcachegrind:

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

¡Muchas gracias por esa pista! En realidad estaba buscando lo mismo. Intentaré. – ThR37

+0

EDITAR: ¡Funciona perfectamente! Un simple contenedor con ctypes está bien, incluso si obtengo segfaults a veces durante el perfil de la CPU (pero esto es "normal" y se explica en el documento ... Estoy usando x86_64 :() – ThR37

+0

Muchas gracias por esta pequeña pepita. muy, muy útil :-) Lo que estoy viendo es que pprof (o más bien, google-pprof en el paquete para Debian), es que no obtengo tantos símbolos exigidos como lo haría cuando perfilé el mismo código con valgrind. ¿Será que necesito especificar -pg al compilar? – miquelramirez

4

Con gprof, puede perfilar cualquier programa que fue properly compiled y ligada (gcc -pg etc, en caso de gprof 's). Si está utilizando una versión Python no construida con gcc (por ejemplo, la versión precompilada de Windows que distribuye la PSF), deberá investigar qué herramientas equivalentes existen para esa plataforma y toolchain (en el caso de Windows PSF, quizás mingw puede ayudar) Puede haber datos "irrelevantes" allí (funciones C internas en el tiempo de ejecución de Python) y, de ser así, los porcentajes mostrados en gprof pueden no ser aplicables, pero los números absolutos (de llamadas y duraciones de los mismos) siguen siendo válidos, y puede postprocesar el resultado de gprof (por ejemplo, con un pequeño script de Python ;-) para excluir los datos irrelevantes y calcular los porcentajes que desea.

+1

Todavía tengo algunos problemas al usar esto, pero tal vez es solo mi culpa. Después de compilar y vincular (gcc) la fuente python el ejecutable produce correctamente el archivo gmon.out. Si ejecuto los scripts que cargan las extensiones C (* .so) compiladas con -pg flags, la salida de generación de perfiles (gprof/path/custom/python, o gprof).so) no muestra las llamadas de función contenidas en la biblioteca C. Me falta algo? – pygabriel

+2

gprof no funciona bien con dlopen(), presumiblemente porque inicializa sus mapas de memoria antes de cargar la biblioteca. La mera compilación con el indicador -pg no hace nada para ayudar a gprof si el servidor exectuable (en este caso, python) no está vinculado directamente con el archivo .so. –

22

Después del comentario de pygabriel decidí subir un paquete para PyPI que implementa un generador de perfiles de extensiones de python utilizando el perfil de CPU de google-perforools: http://pypi.python.org/pypi/yep

+0

¡Gracias, encontré esto realmente útil y bastante fácil de seguir! – robince

+0

Gracias Fabian, con suerte, este es el truco! –

+0

¿Tiene el uso de la memoria de perfil también? –

0

Uno de mis colegas me dijo ltrace(1). Me ayudó bastante en la misma situación.

Asumir el nombre del objeto compartido de su extensión C es myext.so y desea ejecutar benchmark.py, entonces

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

Su salida es como se necesita

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

especial cuidado si su objeto compartido tiene - o + en su nombre de archivo. Estos caracteres no se tratan como están (ver man ltrace(1) para más detalles).

El comodín * puede ser una solución como -x @myext* en lugar de -x @myext-2.so.

Cuestiones relacionadas