Python 为什么非独立numba jitt';教育职能重要吗?
在python中,您可以定义多个以任意顺序相互调用的函数,并且在运行时将调用这些函数。一旦这些函数存在,它们在脚本中定义的顺序就无关紧要了。例如,下面的选项是有效的,可以使用Python 为什么非独立numba jitt';教育职能重要吗?,python,jit,numba,Python,Jit,Numba,在python中,您可以定义多个以任意顺序相互调用的函数,并且在运行时将调用这些函数。一旦这些函数存在,它们在脚本中定义的顺序就无关紧要了。例如,下面的选项是有效的,可以使用 import numpy as np def func1(arr): out = np.empty_like(arr) for i in range(arr.shape[0]): out[i] = func2(arr[i]) # calling func2 here which is d
import numpy as np
def func1(arr):
out = np.empty_like(arr)
for i in range(arr.shape[0]):
out[i] = func2(arr[i]) # calling func2 here which is defined below
return out
def func2(a):
out = a + 1
return out
func1
可以调用func2
,即使func2
是在func1
之后定义的
然而,如果我用修饰这些函数,我会得到一个错误
import numpy as np
import numba as nb
@nb.jit("f8[:](f8[:])", nopython=True)
def func1(arr):
out = np.empty_like(arr)
for i in range(arr.shape[0]):
out[i] = func2(arr[i])
return out
@nb.jit("f8(f8)", nopython=True)
def func2(a):
out = a + 1
return out
>>> TypingError: Failed in nopython mode pipeline (step: nopython frontend)
Untyped global name 'func2': cannot determine Numba type of <class
'numba.ir.UndefinedType'>
为什么会这样?我觉得纯python模式可以工作,因为python是动态类型化的,而不是编译的,而numba使用JIT,根据定义编译函数(因此可能需要对每个函数中发生的一切都有全面的了解?)。但我不明白,如果numba遇到一个它没有看到的函数,为什么它不在范围内搜索所有函数。短版本-删除
“f8[:](f8[:])”
你的直觉是对的。Python函数是在调用时查找的,这就是为什么它们可以无序定义的原因。使用dis
(反汇编)模块查看python字节码可以清楚地表明这一点-每次调用函数a
时,都会将名称b
作为全局查找
def a():
return b()
def b():
return 2
import dis
dis.dis(a)
# 2 0 LOAD_GLOBAL 0 (b)
# 2 CALL_FUNCTION 0
# 4 RETURN_VALUE
在nopython模式下,numba需要静态地知道被调用的每个函数的地址——这使代码变得快速(不再执行运行时查找),同时也为其他优化打开了大门,如内联
也就是说,numba可以处理这个案子。通过指定类型签名(
“f8[:]”(f8[:])“
),可以强制提前编译。省略它,一个数字将根据第一个函数调用它,它将工作。感谢您的解释。我确实注意到,省略签名(但仍然用jit装饰函数)似乎是可行的。但是,对于我的用例,我将保留签名,因为我希望在开始对函数进行操作之前获得正确的错误消息,以防将某些不正确的类型传递给函数。再次感谢@PyRsquared还请注意,显式声明非连续数组[:]通常会阻止SIMD矢量化,并可能导致运行时变慢(只需将out=a+1
替换为out=np.sin(a)
,即可看到相当明显的效果。但如果显式声明连续数组[::1]该函数在非连续数组上不再工作。@max9111非常好的观察,谢谢!因此,在本例中,为了获得最佳性能(使用SIMD),您建议使用f[::1]
?我不完全确定numpy数组在内存中的存储方式,但是对于@PyRsquared,您可以使用ndarray.flags(例如A=B[0:-1:2])检查这些内容->A是B)上的非连续视图。您必须选择它。如果您可以将函数限制为连续数组…另一种可能是使用np.ASCONTIGOUUSARRAY,但这有复制数组的缺点。有时您需要显式创建连续副本,例如,有时不需要。这取决于算法。。。
def a():
return b()
def b():
return 2
import dis
dis.dis(a)
# 2 0 LOAD_GLOBAL 0 (b)
# 2 CALL_FUNCTION 0
# 4 RETURN_VALUE