我已经开发了一个Python C扩展,从Python中接收数据并计算一些CPU密集型的计算。
可以对C扩展进行性能分析吗?
问题在于,编写一个用于性能分析的C语言示例测试可能很具有挑战性,因为代码依赖于特定的输入和数据结构(由Python控制代码生成)。
你有什么建议吗?
在 pygabriel 的评论之后,我决定上传一个使用来自 google-perftools 的 cpu-profiler 实现 python 扩展程序的分析器包到 pypi 上: http://pypi.python.org/pypi/yep
我已经找到了解决方法,使用google-perftools。窍门是用Python包装StartProfiler和StopProfiler函数(在我的情况下通过Cython)。
要对C扩展进行分析,只需将Python代码包装在StartProfiler和StopProfiler调用中即可。
from google_perftools_wrapped import StartProfiler, StopProfiler
import c_extension # extension to profile c_extension.so
StartProfiler("output.prof")
... calling the interesting functions from the C extension module ...
StopProfiler()
例如,要进行分析,您可以以callgrind格式导出,然后在kcachegrind中查看结果:
pprof --callgrind c_extension.so output.prof > output.callgrind
kcachegrind output.callgrind
我的同事告诉我使用ltrace(1)
,在相同的情况下帮助了我很多。
假设你的C扩展共享对象名称为myext.so
,并且你想执行benchmark.py
,那么:
ltrace -x @myext.so -c python benchmark.py
% 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
....
如果您的共享对象文件名中包含-
或+
字符,则需要特别注意。这些字符不会被视为正常字符(详见man 1 ltrace
)。
通配符*
可以作为解决方法,例如将-x @myext-2.so
替换为-x @myext*
。
使用 gprof,您可以对任何已经 正确编译和链接(例如,在 gprof
中使用的 gcc -pg
)的程序进行分析。如果您使用的是未使用 gcc
编译的 Python 版本(例如,PSF 发布的预编译 Windows 版本),您需要研究该平台和工具链中存在哪些等效工具(在 Windows PSF 的情况下,也许 mingw
可以帮助)。可能会有“无关”数据(Python 运行时的内部 C 函数),如果有,则 gprof
显示的百分比可能不适用,但是调用次数和持续时间的绝对数字仍然有效,并且您可以后处理 gprof
的输出(例如,使用一个小 Python 脚本;-) 来排除无关数据并计算所需的百分比。