以下是代码和结果:
python -c "import numpy as np; from timeit import timeit; print('numpy version {}: {:.1f} seconds'.format(np.__version__, timeit('np.random.multinomial(1, [0.1, 0.2, 0.3, 0.4])', number=1000000, globals=globals())))"numpy version 1.16.6: 1.5 seconds # 10x faster
numpy version 1.18.1: 15.5 seconds
numpy version 1.19.0: 17.4 seconds
numpy version 1.21.4: 15.1 seconds需要注意的是,在固定随机种子的情况下,不同的numpy版本的输出是相同的。
python -c "import numpy as np; np.random.seed(0); print(np.__version__); print(np.random.multinomial(1, [0.1, 0.2, 0.3, 0.4], size=10000))" /tmp/tt对于1.16.6之后的numpy版本为什么慢10倍有什么建议吗?
我们已经将熊猫升级到最新版本1.3.4,在1.16.6之后需要numpy版本。
发布于 2021-12-21 04:48:45
TL;DR: --这是由numpy.random.multinomial函数中的附加检查的开销引起的一个局部性能回归。由于所需检查的相对执行时间,非常小的数组受到强烈影响。
在引擎盖下面
对Numpy码的Git提交进行的二进制搜索表明,性能回归在2019年4月中旬首次出现。它可以在提交dd77ce3cb中复制,但不能在7e8e19f9a中复制。提交中间存在一些构建问题,但是通过一些快速修复,我们可以显示提交0f3dd0650是导致问题的第一个原因。提交人说:
扩展多项式以允许广播 修正NumPy中遗漏的zipf更改 启用0作为超几何的有效输入
对此承诺的深入分析表明,它修改了Cython mtrand.pyx中定义的multinomial函数,以执行以下两个额外的检查:
def multinomial(self, np.npy_intp n, object pvals, size=None):
cdef np.npy_intp d, i, sz, offset
cdef np.ndarray parr, mnarr
cdef double *pix
cdef int64_t *mnix
cdef int64_t ni
d = len(pvals)
parr = <np.ndarray>np.PyArray_FROM_OTF(pvals, np.NPY_DOUBLE, np.NPY_ALIGNED)
pix = <double*>np.PyArray_DATA(parr)
check_array_constraint(parr, 'pvals', CONS_BOUNDED_0_1) # <==========[HERE]
if kahan_sum(pix, d-1) > (1.0 + 1e-12):
raise ValueError("sum(pvals[:-1]) > 1.0")
if size is None:
shape = (d,)
else:
try:
shape = (operator.index(size), d)
except:
shape = tuple(size) + (d,)
multin = np.zeros(shape, dtype=np.int64)
mnarr = <np.ndarray>multin
mnix = <int64_t*>np.PyArray_DATA(mnarr)
sz = np.PyArray_SIZE(mnarr)
ni = n
check_constraint(ni, 'n', CONS_NON_NEGATIVE) # <==========[HERE]
offset = 0
with self.lock, nogil:
for i in range(sz // d):
random_multinomial(self._brng, ni, &mnix[offset], pix, d, self._binomial)
offset += d
return multin如果代码是健壮的,那么这两个检查是必需的。然而,考虑到它们的用途,它们目前相当昂贵。
事实上,在我的机器上,第一次检查要支付大约75%的开销,第二次要支付20%的费用。检查只需几微秒,但由于输入非常小,与计算时间相比,开销很大。
解决此问题的一个方法是为此编写一个特定的Numba函数,因为您的输入数组非常小。在我的机器上,np.random.multinomial在一个微不足道的Numba函数中获得了良好的性能。
发布于 2021-12-20 23:36:32
我检查了一些引擎盖下的发电机,发现时间没有多大变化。
我猜想差异可能是由于某些开销造成的,因为您只对单个值进行采样。这似乎是个很好的假设。当我将所生成的随机样本的大小增加到1000时,1.16.6和1.19.2 (我现在的Numpy版本)之间的差距减小到了20%。
python -c "import numpy as np; from timeit import timeit; print('numpy version {}: {:.1f} seconds'.format(np.__version__, timeit('np.random.
multinomial(1, [0.1, 0.2, 0.3, 0.4], size=1000)', number=10000, globals=globals())))"numpy version 1.16.6: 1.1 seconds
numpy version 1.19.2: 1.3 seconds请注意,的两个版本都有这种开销,只是较新的版本具有更大的开销。在这两个版本中,一次采样1000值要比样本1值1000次要快得多。
它们的代码在1.16.6和1.17.0之间发生了很大变化,例如此承诺,很难进行分析。对不起,这不能更好地帮助你--我提议在Numpy's github上提出一个问题。
https://stackoverflow.com/questions/70428623
复制相似问题