【发布时间】:2021-05-14 04:43:31
【问题描述】:
以下用于逻辑比较的 numba 编译函数中性能下降的原因可能是什么:
from numba import njit
t = (True, 'and_', False)
#@njit(boolean(boolean, unicode_type, boolean))
@njit
def f(a,b,c):
if b == 'and_':
out = a&c
elif b == 'or_':
out = a|c
return out
x = f(*t)
%timeit f(*t)
#1.78 µs ± 9.52 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit f.py_func(*t)
#108 ns ± 0.0042 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
按照答案中的建议进行大规模测试:
x = np.random.choice([True,False], 1000000)
y = np.random.choice(["and_","or_"], 1000000)
z = np.random.choice([False, True], 1000000)
#using jit compiled f
def f2(x,y,z):
L = x.shape[0]
out = np.empty(L)
for i in range(L):
out[i] = f(x[i],y[i],z[i])
return out
%timeit f2(x,y,z)
#2.79 s ± 86.4 ms per loop
#using pure Python f
def f3(x,y,z):
L = x.shape[0]
out = np.empty(L)
for i in range(L):
out[i] = f.py_func(x[i],y[i],z[i])
return out
%timeit f3(x,y,z)
#572 ms ± 24.3 ms per
我是否遗漏了什么,是否有办法编译“快速”版本,因为这将成为循环执行 ~ 1e6 次的一部分。
【问题讨论】:
-
1.您始终可以在 c 中实现并调用 dll。 2.我会预先将“and_”和“or_”标记化(也许这可以向量化),并根据标记调用正确的函数,从而跳过
if-elif语句。 -
这听起来像是一个简单的编译器版本。我支持首先标记化的建议,一旦你有标记化的代码,当标记类型已知时,对每个标记进行操作。例如,如果您有一个“And”标记和一个“Or”标记,两者都使用
.op()方法,您将能够先标记化然后.op(),这将永远不会执行if-else。这是一个 2-pass 算法。 -
如果这必须是一个单通道算法,我相信它可以在某种前瞻的帮助下完成。无论如何,如果这对性能要求很高,省去你的麻烦,用 c 或 c++ 编写,只从 Python 调用
-
地图don't work。两个版本都有
if/else。 -
您是否尝试定义继承结构,例如
BaseOperator(Object), def op(*args)、And(BaseOperator)Or(BaseOperator)?然后定义静态类型。
标签: python performance compiler-construction numba