簡體   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