繁体   English   中英

用Cython进行线性分析内部功能

[英]Line Profiling inner function with Cython

使用此答案来分析我的Cython代码,我已经取得了很大的成功,但是它似乎无法与嵌套函数一起正常工作。 此笔记本中,您可以看到在嵌套函数上使用线剖析器时,剖析没有出现。 有没有办法让它工作?

tl,dr:

Cython似乎是一个问题,有一种Cython的方法可以解决问题,但并不可靠,您可以将它用于一次性情况,直到解决此问题为止*

更改line_profiler源:

我不能百分百确定这一点,但是它能正常工作,您需要做的是下载line_profiler的源代码,然后python_trace_callbackpython_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_codeb''Cython ,所以在比赛的本质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生成的文件存储在临时目录中) 。

因此,您的确获得了代码的性能分析统计信息,但没有任何内容。 一个人也许可以在两个有问题的代码对象之间强行复制文件名,但这是另一个问题。

*问题:

这里的问题是由于某种原因,在处理嵌套和/或封闭函数时,在创建代码对象以及在Python框架之一中解释代码对象时,其地址存在异常。 您遇到的问题是由于不满足以下条件引起的:

    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从当前执行帧中抢夺的时, 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以及在另一个函数中定义一个函数时,才会发生这种情况。 这或者我完全不了解的东西。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM