Numba-nopython模式比对象模式慢?

Numba-nopython模式比对象模式慢?,python,numba,Python,Numba,首先在这里使用Numba。我读到nopython模式应该产生更快的代码,但是: @jit(float64[:](int64[:], int64, float64), nopython=True) def epsilon_bound(l, k, delta): return l/k+np.sqrt(np.log(1/delta))*np.sqrt(1/(2*k)) @jit(float64(float64, int64, float64), nopython=False) def sim

首先在这里使用Numba。我读到nopython模式应该产生更快的代码,但是:

@jit(float64[:](int64[:], int64, float64), nopython=True)
def epsilon_bound(l, k, delta):
    return l/k+np.sqrt(np.log(1/delta))*np.sqrt(1/(2*k))

@jit(float64(float64, int64, float64), nopython=False)
def sim_bin(epsilon, sims, delta):
    k=10000
    s = np.random.binomial(k, epsilon, size=(sims,))
    print(nb.typeof(s))
    bound = epsilon_bound(s, k, delta)
    violations = np.greater(epsilon, bound)
    return np.sum(violations)/float(sims)

%%time
a = sim_bin(0.1, 1_000_000, 0.1)
运行速度要快得多:

array(int64, 1d, C)
CPU times: user 66.7 ms, sys: 0 ns, total: 66.7 ms
Wall time: 65.7 ms
除此之外:

@jit(float64[:](int64[:], int64, float64), nopython=True)
def epsilon_bound(l, k, delta):
    return l/k+np.sqrt(np.log(1/delta))*np.sqrt(1/(2*k))

@jit(float64(float64, int64, float64), nopython=True)
def sim_bin(epsilon, sims, delta):
    k=10000
    s = np.random.binomial(k, epsilon, size=(sims,))
    #print(nb.typeof(s))
    bound = epsilon_bound(s, k, delta)
    violations = np.greater(epsilon, bound)
    return np.sum(violations)/float(sims)

CPU times: user 4.94 s, sys: 8.02 ms, total: 4.95 s
Wall time: 4.93 s
运行sim_bin.inspect_types()显示第一个选项使用所有pyobjects,而第二个选项正确推断所有类型。根据文档()说明,nopython模式应该可以生成更快的代码。 有人知道发生了什么事吗?一定有一个很好的理由,但我对使用麻木是新手。是因为我主要使用矢量化的numpy函数吗


谢谢

该函数的一个主要瓶颈(至少对于numba 0.31,windows 10)似乎是
np.random.binomial
-调用。当我测试它时:

@jit(nopython=True)
def nbbinom():
    return np.random.binomial(10000, 0.1, size=(1000000,))

nbbinom() # warmup
%timeit nbbinom()
# 1 loop, best of 3: 2.45 s per loop
%timeit np.random.binomial(10000, 0.1, size=(1000000,))
# 10 loops, best of 3: 23.1 ms per loop
然而,这可能取决于numba版本。Numba(不幸的是)经常遭受(幸运的是)快速修复的性能退化。可能它只需要他们的bug追踪器上的一个问题(如果还没有修复的话)

但即使这样,您的代码也包含许多向量化操作。如果你使用矢量化操作,你将不会得到太多的速度提升。根据经验:如果您已经可以在不使用python循环的情况下使用numpy,那么您不需要NUBA(也有例外。例如:我发现numpy UFUNC具有显著开销的小型阵列的NUBA速度明显更快)

另一件事是创建随机数组所需的时间(以numpy和numba为单位)比实际操作要长得多:

import numpy as np
from numba import njit

@njit
def epsilon_bound1(l, k, delta):
    return l/k+np.sqrt(np.log(1/delta))*np.sqrt(1/(2*k))

def epsilon_bound2(l, k, delta):
    return l/k+np.sqrt(np.log(1/delta))*np.sqrt(1/(2*k))

def sim_bin(s, k, epsilon, sims, delta, func):
    bound = func(s, k, delta)
    violations = np.greater(epsilon, bound)
    return np.sum(violations)/float(sims)

epsilon = 0.1
sims = 1000000
delta = 0.1
k=10000
s = np.random.binomial(k, epsilon, size=(sims,))
%timeit np.random.binomial(k, epsilon, size=(sims,))
# 1 loop, best of 3: 232 ms per loop

sim_bin(s, k, 0.1, 1000000, 0.1, epsilon_bound1)  # warmup
%timeit sim_bin(s, k, 0.1, 1000000, 0.1, epsilon_bound1)
# 10 loops, best of 3: 28.5 ms per loop
%timeit sim_bin(s, k, 0.1, 1000000, 0.1, epsilon_bound2)
# 10 loops, best of 3: 37.6 ms per loop

因此,当您对
sim_-bin
进行基准测试时,实际上您只对
np.random.binomial
的调用进行基准测试,无论是相对较快的numpy实现还是(当前)相当缓慢的numba实现。

当我运行这两段代码时,我看不到性能差异。但是,一般来说,您希望忽略numba jitted函数的第一次运行时间,因为这包括jitting时间(因此使用
%timeit
,而不是
%time
)。@JoshAdel通过提供签名,您可以预编译函数,因此编译运行应该是不必要的。我看不到这些调用之间的时间差,但我确实可以看到jitted
np.random.binomial
和未连接调用之间的时间差。对不起,我忘了包括我使用的python和numba的版本。linux上的python 3.6、numba 0.31和numpy 1.12。我将检查此回归是否已修复。谢谢你的帮助。非常感谢!我不知道numba重新实现了随机二项式。我怀疑我不会得到太多的加速,但当我看到它跑得较慢时,我非常失望。既然我知道这不应该发生,我将把它作为一个bug发布。我尝试在epsilon_bound上使用vectorize而不是jit,但结果慢了50%。你会说这些性能差异是可以预测的(一旦你了解幕后发生的事情),还是说这是一个反复试验的问题?我在numba 0.26和0.25中测试了你的nbbinom比较,结果比你在0.31中看到的random.Binomian慢。我猜这不是一个回归,只是一个较慢的实现。我也试过v0.21和v0.23,但它不会运行。@Luk17关于可预测性:numba不可能比numpy做得更好。只是因为如果它可以更快(除了并行执行),那么numpy可能也会使用更快的方式。然而,当您被numpy困住并尝试使用python循环时,可能应该使用cython/numba。然而,人们无法预测类似于
np.random.binomial
,这是一个意想不到的性能不佳的函数。:)