是否可以将Line_Profiler与Numba一起使用?
在用%lprun
修饰的函数上调用@numba.jit
将返回空配置文件:
Timer unit: 1e-06 s
Total time: 0 s
File: <ipython-input-29-486f0a3cdf73>
Function: conv at line 1
Line # Hits Time Per Hit % Time Line Contents
==============================================================
1 @numba.jit
2 def conv(f, w):
3 f_full = np.zeros(np.int(f.size + (2 * w.size) - 2), dtype=np.float64)
4 for i in range(0, f_full.size):
5 if i >= w.size - 1 and i < w.size + f.size - 1:
6 f_full[i] = f[i - w.size + 1]
7 w = w[::-1]
8 g = np.zeros(f_full.size-w.size + 1, dtype=np.float64)
9 for i in range(0, f_full.size - w.size):
10 g[i] = np.sum(np.multiply(f_full[i:i+w.size], w))
11 return g
有一个Cython代码的解决方案,但是找不到Numba的任何东西。
最佳答案
tl;dr:对numba函数进行行分析(技术上)可能不可能,但即使可以对numba函数进行行分析,结果也可能不准确。
分析器和编译/优化语言的问题
将分析器与“已编译”语言一起使用是很复杂的(甚至在某种程度上还可以使用非编译语言,具体取决于允许运行时执行的操作),因为编译器可以重写代码。举几个例子:constant folding,inline function calls,unroll loops(利用SIMD instructions),hoisting,通常对表达式重新排序/重新排列(甚至在多行上)。一般来说,只要结果和副作用"as if"函数没有“优化”,编译器就可以做任何事情。
示意图:
+---------------+ +-------------+ +----------+
| Source file | -> | Optimizer | -> | Result |
+---------------+ +-------------+ +----------+
这是一个问题,因为探查器需要在代码中插入语句,例如,函数探查器可能会在每个函数的开始和开始处插入语句,即使代码经过优化并且函数是内联的,也可能会起作用,因为“探查器语句”也是内联的但是,如果编译器由于附加的profiler语句而决定不内联函数呢那么你所描述的可能与“真正的程序”的执行方式不同。
例如,如果您有(我在这里使用python,尽管它没有编译,但假设我用c编写了这样一个程序):
def give_me_ten():
return 10
def main():
n = give_me_ten()
...
然后优化器可以将其重写为:
def main():
n = 10 # <-- inline the function
但是,如果插入探查器语句:
def give_me_ten():
profile_start('give_me_ten')
n = 10
profile_end('give_me_ten')
return n
def main():
profile_start('main')
n = give_me_ten()
...
profile_end('main')
优化器可能只是发出相同的代码,因为它没有内联函数。
实际上,行分析器会在代码中插入更多的“分析器语句”。在每一行的开头和结尾。这可能会阻止许多编译器优化。我不太熟悉“好像”规则,但我的猜测是,那时很多优化是不可能的。因此,使用profiler编译的程序的行为将与不使用profiler编译的程序的行为大不相同。
例如,如果您有此程序:
def main():
n = 1
for _ in range(1000):
n += 1
...
优化器可以(不确定是否有编译器会这样做)将其重写为:
def main():
n = 1001 # all statements are compile-time constants and no side-effects visible
但是,如果有行分析语句,则:
def main():
profile_start('main', line=1)
n = 1
profile_end('main', line=1)
profile_start('main', line=2)
for _ in range(1000):
profile_end('main', line=2)
profile_start('main', line=3)
n += 1
profile_end('main', line=3)
profile_start('main', line=2)
...
然后根据“仿佛”规则,循环有副作用,不能压缩为单个语句(也许代码仍然可以优化,但不能作为单个语句)。
注意,这些都是简单的例子,编译器/优化器通常非常复杂,并且有很多可能的优化。
根据语言、编译器和分析器的不同,可以减轻这些影响。但是,面向Python的分析器(如行分析器)不可能针对C/C++编译器。
还要注意,这并不是Python真正的问题,因为Python只是一步一步地执行一个程序(不是真的,但是Python很少更改您的“编写的代码”,而且只是以很小的方式)。
这怎么适用于麻木和赛松?
Cython将您的Python代码转换成C(或C++)代码,然后使用C(或C++)编译器编译它。示意图:
+-------------+ +--------+ +----------+ +-----------+ +--------+
| Source file | -> | Cython | -> | C source | -> | Optimizer | -> | Result |
+-------------+ +--------+ +----------+ +-----------+ +--------+
numba根据参数类型转换python代码,并使用llvm编译代码。示意图:
+-------------+ +-------+ +------------------+ +--------+
| Source file | -> | Numba | -> | LLVM / Optimizer | -> | Result |
+-------------+ +-------+ +------------------+ +--------+
两者都有一个可以进行广泛优化的编译器。如果在编译代码之前将分析语句插入到代码中,许多优化都是不可能的。因此,即使可以对代码进行行配置,结果也可能不准确(在实际程序将以这种方式执行的意义上是准确的)。
line profiler是为纯python编写的,所以如果它有效的话,我不一定相信cython/numba的输出。它可能会给出一些提示,但总的来说可能太不准确了。
尤其是numba可能非常棘手,因为numba翻译程序需要支持分析语句(否则您将最终得到一个对象模式的numba函数,这将产生完全不准确的结果),而jitted函数不再只是一个函数。它实际上是一个分派器,根据参数的类型委托给一个“隐藏”函数因此,当您使用
int
或float
调用同一个“dispatcher”时,它可以执行完全不同的函数。有趣的事实:使用函数剖析器进行剖析的行为已经带来了巨大的开销,因为numba开发人员想要让它工作(参见cProfile adds significant overhead when calling numba jit functions)。好吧,怎么描述他们?
您可能应该使用可以与编译器一起处理已翻译代码的探查器进行探查与为Python代码编写的探查器相比,这些方法可以(可能)产生更精确的结果这将更加复杂,因为这些探查器将返回转换后的代码的结果,这些结果必须再次手动传输到原始代码。此外,它甚至可能是不可能的-通常Cython/Numba管理结果的翻译、编译和执行,因此您需要检查它们是否为附加的profiler提供了钩子我在那里没有经验。
一般来说:如果你有优化器,那么总是把利润当作“指南”,而不一定是“事实”并且始终使用专为编译器/优化器设计的探查器,否则会失去很多可靠性和/或准确性。
关于python - 将line_profiler与numba jitted函数配合使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54545511/