使用 Cython 进行线分析内部函数
Line Profiling inner function with Cython
我使用 to profile my Cython code, but it doesn't seem to work properly with nested functions. In this notebook 取得了相当大的成功,您可以看到当在嵌套函数上使用行分析器时,配置文件不会出现。有没有办法让它发挥作用?
tl,博士:
这似乎是 Cython
的一个问题,有一种骇人听闻的方法可以解决问题,但并不可靠,在解决此问题之前,您可以将其用于一次性案例*
更改 line_profiler
来源:
我不能 100% 确定它是否有效,您需要做的是 download the source for line_profiler and go fiddle around in 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
内容匹配的代码对象。 co.co_code
对于 Cython
是 b''
,所以本质上匹配具有该名称的 Cython
函数。这是它可以变得更健壮并匹配 code
对象的更多属性(例如,文件名)的地方。
然后您可以使用 python setup.py build_ext
构建它并使用 sudo python setup.py install
安装它。 我目前正在使用 python setup.py build_ext --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
生成。
因此,您确实获得了代码的分析统计信息,但没有获得任何内容。或许可以在有问题的两个代码对象之间强制复制文件名,但这完全是另一个问题。
*问题:
这里的问题是,由于某些原因,在处理嵌套的and/or封闭函数时,代码对象在创建时和在其中一个Python中被解释时的地址存在异常帧。您遇到的问题是由以下原因引起的 condition not being satisfied:
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
并且当一个函数定义在另一个函数中时。要么是这个,要么是我完全缺少的东西。
我使用
tl,博士:
这似乎是 Cython
的一个问题,有一种骇人听闻的方法可以解决问题,但并不可靠,在解决此问题之前,您可以将其用于一次性案例*
更改 line_profiler
来源:
我不能 100% 确定它是否有效,您需要做的是 download the source for line_profiler and go fiddle around in 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
内容匹配的代码对象。 co.co_code
对于 Cython
是 b''
,所以本质上匹配具有该名称的 Cython
函数。这是它可以变得更健壮并匹配 code
对象的更多属性(例如,文件名)的地方。
然后您可以使用 python setup.py build_ext
构建它并使用 sudo python setup.py install
安装它。 我目前正在使用 python setup.py build_ext --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
生成。
因此,您确实获得了代码的分析统计信息,但没有获得任何内容。或许可以在有问题的两个代码对象之间强制复制文件名,但这完全是另一个问题。
*问题:
这里的问题是,由于某些原因,在处理嵌套的and/or封闭函数时,代码对象在创建时和在其中一个Python中被解释时的地址存在异常帧。您遇到的问题是由以下原因引起的 condition not being satisfied:
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
并且当一个函数定义在另一个函数中时。要么是这个,要么是我完全缺少的东西。