Python:如何对使用numba.njit()修饰符编写的代码进行性能分析?

12
我有一个相当复杂的计算代码,正在尝试加速和多线程化。为了优化代码,我正在努力确定哪些函数需要最长时间或被调用最多。
我以前没有对代码进行过分析,所以可能会漏掉一些东西。然而,我知道许多现有的分析模块与 numba 的 njit() 装饰器不太兼容,因为需要重新编译 LLVM。
因此,我的问题是:在大多数函数都有 njit() 装饰器,而只有少数非 jitted 控制函数的代码中,最好的分析代码的方法是什么?
我之前遇到过 data_profiler,但它似乎不再在 conda 存储库中,并且我不知道如何在 conda 中构建它,或者它是否仍与其依赖项的现代版本兼容。

你尝试过在 njit 中设置 cache=True 选项吗?借助像 Spyder IDE 中实现的分析器,它内部使用 cProfile,这也是一种手动分析的好模块。 - JE_Muc
1个回答

4

如果没有别的办法,让我们试一试:

在QuantFX模块开发中,我们花费了数十年的时间,使用过numba和其他向量化/ jit加速工具。让我分享一些经验,这些经验被认为对于我们类似的分析很有用。

与提到的data_profiler相反,我们享受使用ZeroMQ模块所提供的微秒级分辨率,用于分布式信令/消息基础设施。

ZeroMQ将其所有服务实现在一个名为Context的核心引擎中,但有一个小型实用程序可独立于此仪器重复使用,即Stopwatch - 一个微秒级分辨率计时器类。

因此,没有什么能阻止我们:

from pyzmq import Stopwatch as MyClock

aClock_A = MyClock(); aClock_B = MyClock(); aClock_C = MyClock(); print( "ACK: A,B,C made" )

# may use 'em when "framing" a code-execution block:
aClock_A.start(); _ = sum( [ aNumOfCollatzConjectureSteps( N ) for N in range( 10**10 ) ] ); TASK_A_us = aClock_A.stop()
print( "INF: Collatz-task took {0:} [us] ".format( TASK_A_us ) )

# may add 'em into call-signatures and pass 'em and/or re-use 'em inside whatever our code
aReturnedVALUE = aNumbaPreCompiledCODE(  1234,
                                        "myCode with a need to profile on several levels",
                                        aClock_A, #     several, 
                                        aClock_B, # pre-instantiated,
                                        aClock_C  #     Stopwatch instances, so as
                                        )         #  to avoid chained latencies

通过至少使用这个工具作为最后一道防线,可以将任何基于Stopwatch的性能分析结构“硬编码”到自己的源代码中。唯一的限制是需要符合Stopwatch实例的有限状态自动机,其中一旦调用了.start()方法,只能接下来调用.stop()方法,并且类似地,在尚未.start()的实例上调用.stop()方法将自然引发异常。
常见的try-except-finally脚手架将有助于确保所有Stopwatch实例再次.stop(),即使可能发生异常。
“硬编码”性能分析的结构取决于您的代码执行“热点”测试和跨边界调用相关开销的性能分析,这些开销在@jit装饰的numba-LLVM-ed代码的本机python调用和启动numba编译代码内部的第一行之间花费的时间(即在调用调用和参数分析之间需要多长时间,由调用签名列表驱动或通过强制使用单个显式调用签名来避免)。
祝你好运。希望这可以帮到你。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接