使用Cython进行线性剖析内部函数
我用这个答案来分析我的Cython代码已经取得了相当不错的成功,但它似乎无法与嵌套函数正常工作。 在这个笔记本中,你可以看到当在一个嵌套函数上使用线剖析器时,配置文件没有出现。 有没有办法让这个工作?
TL博士:
这似乎是Cython
一个问题,存在一种伎俩,但不可靠,你可以将它用于一次性案例,直到这个问题得到解决*
更改line_profiler
源代码:
我不能100%确定,但它工作,你需要做的就是下载line_profiler的源代码,并在python_trace_callback
中python_trace_callback
。 在从当前执行帧( code = <object>py_frame.f_code
)获取code
对象后,添加以下内容:
if what == PyTrace_LINE or what == PyTrace_RETURN:
code = <object>py_frame.f_code
# Add entry for code object with different address if and only if it doesn't already
# exist **but** the name of the function is in the code_map
if code not in self.code_map and code.co_name in {co.co_name for co in self.code_map}:
for co in self.code_map:
# make condition as strict as necessary
cond = co.co_name == code.co_name and co.co_code == code.co_code
if cond:
del self.code_map[co]
self.code_map[code] = {}
这会将self.code_map
的代码对象替换为当前正在执行的与其名称和co.co_code
内容匹配的代码对象。 对于Cython
, co.co_code
是b''
,所以本质上在用这个名字匹配Cython
函数。 这里可以变得更健壮,并匹配code
对象的更多属性(例如,文件名)。
然后你可以用python setup.py build_ext
build_ext来构建它,并使用sudo python setup.py install
。 我目前正在用python setup.py build_ext --inplace
构建它 - 为了在本地使用它,我建议你也这样做。 如果您使用--inplace
构建它, --inplace
确保在import
之前导航到包含line_profiler
源文件的文件夹。
因此,在包含line_profiler
的内置共享库的文件夹中,我设置了一个包含函数的cyclosure.pyx
文件:
def outer_func(int n):
def inner_func(int c):
cdef int i
for i in range(n):
c+=i
return c
return inner_func
和一个相当的setup_cyclosure.py
脚本来构建它:
from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize
from Cython.Compiler.Options import directive_defaults
directive_defaults['binding'] = True
directive_defaults['linetrace'] = True
extensions = [Extension("cyclosure", ["cyclosure.pyx"], define_macros=[('CYTHON_TRACE', '1')])]
setup(name = 'Testing', ext_modules = cythonize(extensions))
如前所述,构建是使用python setup_cyclosure.py build_ext --inplace
。
从当前文件夹启动您的解释器并发出以下命令,即可得到想要的结果:
>>> import line_profiler
>>> from cyclosure import outer_func
>>> f = outer_func(5)
>>> prof = line_profiler.LineProfiler(f)
>>> prof.runcall(f, 5)
15
>>> prof.print_stats()
Timer unit: 1e-06 s
Total time: 1.2e-05 s
File: cyclosure.pyx
Function: inner_func at line 2
Line # Hits Time Per Hit % Time Line Contents
==============================================================
2 def inner_func(int c):
3 cdef int i
4 1 5 5.0 41.7 for i in range(n):
5 5 6 1.2 50.0 c+=i
6 1 1 1.0 8.3 return c
问题与IPython %%cython
:
试图从IPython
运行这个结果会导致不幸的情况。 在执行时, code
对象不会保存定义文件的路径,它只是存储文件名。 由于我简单地将code
对象放到self.code_map
字典中,并且由于代码对象具有只读属性,因此从IPython
使用它时会丢失文件路径信息(因为它将%%cython
生成的文件存储在临时目录中) 。
因此,您确实获得了代码的分析统计信息,但是没有内容。 有人可能能够强制复制有问题的两个代码对象之间的文件名,但这完全是另一个问题。
*问题:
这里的问题在于,由于某些原因,在处理嵌套函数和/或封闭函数时,代码对象的地址在创建时以及在一个Pythons框架中被解释时存在异常。 您遇到的问题是由以下条件不满足造成的:
if code in self.code_map:
这很奇怪。 在IPython
创建函数并将其添加到LineProfiler
确实将其添加到self.code_map
字典中:
prof = line_profiler.LineProfiler(f)
prof.code_map
Out[16]: {<code object inner_func at 0x7f5c65418f60, file "/home/jim/.cache/ipython/cython/_cython_magic_1b89b9cdda195f485ebb96a104617e9c.pyx", line 2>: {}}
当实际测试前一个条件的时候,并且当前代码对象从当前执行框架被code = <object>py_frame.f_code
,代码对象的地址是不同的:
# this was obtained with a basic print(code) in _line_profiler.pyx
code object inner_func at 0x7f7a54e26150
表明它被重新创建。 这只会发生在Cython
和一个函数被定义在另一个函数中。 无论是这个还是我完全缺失的东西。
上一篇: Line Profiling inner function with Cython
下一篇: Python: When should I ever use file.read() or file.readlines()?