Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/283.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python numba编译逻辑比较中的性能损失_Python_Performance_Compiler Construction_Numba - Fatal编程技术网

Python numba编译逻辑比较中的性能损失

Python numba编译逻辑比较中的性能损失,python,performance,compiler-construction,numba,Python,Performance,Compiler Construction,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 =

以下代码中性能下降的原因是什么?编译函数用于逻辑比较:

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)

要在回答中建议的范围内进行测试:

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

我是否遗漏了什么,是否有办法编译“快速”版本,因为这将是循环执行~1e6次的一部分。

您的工作粒度太小。Numba不是为此而设计的。您看到的几乎所有执行时间都来自于包装/展开参数、类型检查、Python函数包装、引用计数等的开销。此外,使用Numba的好处非常小,因为Numba几乎没有优化unicode字符串操作

检查此假设的一种方法是只执行以下简单函数:

@njit
def f(a、b、c):
归还
x=f(真,'和',假)
%timeit f(True'和uu',False)
普通函数和原始版本在我的机器上都需要1.34µs

此外,您还可以反汇编Numba函数,查看执行一次调用所需的指令量,并深入了解开销的来源

如果希望Numba有用,则需要在编译后的函数中添加更多工作,可能需要直接处理数组/列表。如果由于输入类型的动态特性,这是不可能的,那么Numpy在这里可能不是合适的工具。您可以尝试重新编写一点代码,改用PyPy。编写本机C/C++模块可能会有所帮助,但大部分时间将花在操作动态对象和unicode字符串以及进行类型自省上,除非重写整个代码


更新

上述开销仅在从Python类型转换到Numba(反之)时支付。通过以下基准,您可以看到:

@njit
def f(a、b、c):
如果b=='和':
out=空调
elif b=='或u':
out=a | c
返回
@准时制
def多呼叫(a、b、c):
res=真
对于范围(1_000_000)内的i:
res^=f(a,b,c^res)
返回res
t=(真,'和',假)
x=多个呼叫(*t)
%timeit manycall(*t)

在我的机器上调用多个调用需要3.62毫秒。这意味着每次调用
f
平均需要3.6ns(16个周期)。这意味着开销只支付一次(当调用
manycall
时)。

1。您总是可以用c实现并调用dll。2.我会事先标记“and”和“or”(也许这可以矢量化),并基于标记调用正确的函数,从而跳过
if elif
语句。这听起来像是一个简单的编译器版本。我支持首先标记化的建议,一旦您有了标记化的代码,在已知标记类型的情况下对每个标记执行操作。例如,如果您有一个带有
.op()
方法的“And”标记和一个“Or”标记,那么您将能够首先标记,然后
.op()
这将永远不会执行
If else
。这是一个2遍算法。如果这必须是一个单遍算法,我相信它可以通过某种前瞻性的帮助来完成。不管怎样,如果这是非常苛刻的性能,那就省省你自己的头痛,然后用C或C++写,只从Python映射调用。两个版本都有
if/else
。您是否尝试定义继承结构,例如
BaseOperator(Object)、def op(*args)
和(BaseOperator)
或(BaseOperator)
?然后定义静态类型。谢谢您的时间!毫无疑问,numba将在冗长的矢量化循环中大放异彩。问题是如何正确地编译将被送入循环的逻辑解析器,是否有可能恢复10倍的性能损失(最终将提供净增益)。问题不是真正的10倍性能损失。如果解析代码也是JIT的,您可以轻松地删除它。主要问题是Numba是否真的可以提高解析代码的性能,我不确定这是否可行,主要是因为输入本身:动态类型和递归对Numba来说真的不好。Numba在这里能提供帮助的唯一情况是,对于许多不同的
X
,表达式
t
是相同的。在这种情况下,您可以专门为给定的
t
生成编译函数。否则,我的建议是使用另一个工具或用C/C++重写解析器。我不希望对10条语句的逻辑进行性能改进。10倍的减速乘以1e6让我警觉。你可以看到Numba的开销,就像CPython和Numba之间的固定过渡成本一样。另外,当调用方也是(njitted)Numba函数时,不应为调用(njitted)Numba函数支付此费用<代码>1.34µs*1e6=1.34 s是个问题,但支付1.34µs一次就可以了。这就是为什么
\u eval
函数应该使用Numba以及调用
\u eval
的代码的原因。我也更新了我的问题,两次更新都告诉我:在Pyhton和Numba编译代码之间来回发送数据是浪费时间。
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