使用Cython进行线性分析内部函数

C_Z*_*_Z_ 10 python cython nested-function

我使用这个答案来配置我的Cython代码已经取得了相当不错的成功,但它似乎与嵌套函数无法正常工作.在此笔记本中,您可以看到在嵌套函数上使用线轮廓仪时不显示轮廓.有没有办法让这个工作?

Jim*_*ard 8

TL博士:

这似乎是一个问题Cython,有一个hackish方式,但这是不可靠的,你可以将它用于一次性案件,直到这个问题得到解决*

更改line_profiler来源:

我不能100%肯定这一点,但它正在工作,你需要做的是下载line_profiler的源代码,并在其中摆弄python_trace_callback.code从当前执行框架(code = <object>py_frame.f_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] = {}
Run Code Online (Sandbox Code Playgroud)

这将替换self.code_map当前正在执行的与其名称和co.co_code内容匹配的代码对象.co.co_codeb''Cython,所以本质上是匹配Cython函数与该名称.这里它可以变得更健壮并匹配code对象的更多属性(例如,文件名).

然后,您可以使用python setup.py build_ext并安装来构建它sudo python setup.py install.我正在构建它python setup.py build_ext --inplace以便在本地使用它,我建议你也这样做.如果您构建它,请--inplace确保line_profilerimport导入之前导航到包含源的文件夹.

因此,在包含构建的共享库的文件夹中,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
Run Code Online (Sandbox Code Playgroud)

和一个等效的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))
Run Code Online (Sandbox Code Playgroud)

如前所述,构建是使用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  
Run Code Online (Sandbox Code Playgroud)

问题IPython %%cython:

试图运行这个IPython导致一个不幸的情况.在执行时,code对象不会存储定义它的文件的路径,它只是存储文件名.由于我只是将code对象放入self.code_map字典中,并且由于代码对象具有只读属性,因此在使用它时会丢失文件路径信息IPython(因为它存储从%%cython临时目录生成的文件).

因此,您可以获得代码的分析统计信息,但不会获得内容的内容.有人可能会强行复制有问题的两个代码对象之间的文件名,但这完全是另一个问题.

*问题:

这里的问题是,由于某种原因,在处理嵌套和/或封闭函数时,代码对象的地址在创建时以及在Pythons帧之一中被解释时会出现异常.您遇到的问题是由以下条件不满意引起的:

    if code in self.code_map:
Run Code Online (Sandbox Code Playgroud)

这很奇怪.创建你的函数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>: {}}
Run Code Online (Sandbox Code Playgroud)

当实际测试前一个条件的时候,并且从当前执行帧中抢夺当前代码对象时code = <object>py_frame.f_code,代码对象的地址是不同的:

 # this was obtained with a basic print(code) in _line_profiler.pyx
 code object inner_func at 0x7f7a54e26150
Run Code Online (Sandbox Code Playgroud)

表明它被重新创建.只有Cython当函数在另一个函数中定义时才会发生这种情况.无论是这个还是我完全失踪的东西.