下面是代碼和結果:
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 倍的任何建議?
我們已經將 pandas 升級到最新版本 1.3.4,需要 1.16.6 之后的 numpy 版本
uj5u.com熱心網友回復:
我檢查了一些引擎蓋下的發電機,發現時間沒有太大變化。
我猜差異可能是由于一些開銷,因為您只對單個值進行采樣。這似乎是一個很好的假設。當我將生成的隨機樣本的大小增加到 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 的 github 上提出問題。
uj5u.com熱心網友回復:
TL; DR:這是一個本地性能回歸引起的開銷額外檢查在numpy.random.multinomial功能。由于所需檢查的相對執行時間,非常小的陣列會受到強烈影響。
引擎蓋下
對Numpy代碼的Git commits進行二分查找,性能回歸第一次出現是在2019年4月中旬,commit中可以復現,dd77ce3cb但不能復現7e8e19f9a。中間的提交存在一些構建問題,但是通過一些快速修復,我們可以證明提交0f3dd0650是第一個導致問題的。提交說它:
擴展多項式以允許廣播
修復 NumPy 中遺漏的 zipf 更改
啟用 0 作為超幾何的有效輸入
對此提交的更深入分析表明,它修改了multinomialCython 檔案中定義的函式mtrand.pyx以執行以下兩項額外檢查:
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 函式會產生良好的性能。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/389390.html
