如果发生递归,numba 何时进行即时编译?

When does numba just-in-time compile if recursion takes place?

当我有一个函数在不更改类型的情况下递归调用自身,但处理一个大对象(作为参数传递)时...这在幕后的 numba 中如何工作?

我的函数(很小但调用很多)是在第一次调用时即时编译但尚未完成(因为递归)还是仅在第一次调用时才完成编译到功能完成了吗?

例如

@njit
def myfct(large_object):
    a, tail_condition = do_things_1(intermediate_result)
    if tail_condition == True:
       return a
    intermediate_result = myfct(large_object)
    b = do_things_2(a, intermediate_result)
    return b

final_result = myfct(ref_to_large_object)

myfct什么时候编译?它是在第 6 行第二次调用之前就已经编译了,还是仅在我得到 final_result 时才编译并且一切都已经完成了?如果是后者,我该如何避免?

函数每个签名编译一次。如果您不提供显式签名,则在第一次使用每个签名时编译它们:

@nb.njit
def f(n):
    return n * f(n-1) if n > 1 else 1

f(4)           # Compiled here
f(5)           # Already compiled
f(5.1)         # Compiled again

如果您提供显式签名,则在声明时编译函数:

@nb.njit([nb.int32(nb.int32)])
def f(n):
    return n * f(n-1) if n > 1 else 1

f(5)           # Already compiled
f(5.1)         # Crashes. No attempt to compile an additional signature.

提供不止一个显式签名:

@nb.njit([nb.int32(nb.int32), nb.float64(nb.float64)])
def f(n):
    return n * f(n-1) if n > 1 else 1

f(5)           # Already compiled
f(5.1)         # Already compiled

您可以通过在 numba.core.dispatcher.Dispatcher.compile() 中放置一个断点来自行检查。