我正在寫一個新的隨機數發生器,用於numpy的根據任意分佈,當我遇到這確實怪異的行爲來產生隨機數的時候一個巨大的量:地用Cython創建小數組需要
這是考驗。 PYX
setup.py
from distutils.core import setup
from Cython.Build import cythonize
setup(name = "simple cython func",ext_modules = cythonize('test.pyx'),)
配置代碼
#!/usr/bin/python
from __future__ import division
import subprocess
import timeit
#Compile the cython modules before importing them
subprocess.call(['python', 'setup.py', 'build_ext', '--inplace'])
sstr="""
import test
import numpy
u=numpy.random.random(10)
a=numpy.random.random(10)
a=numpy.cumsum(a)
a/=a[-1]
r=numpy.empty(10,int)
"""
print "binary search: creates an array[N] and performs N binary searches to fill it:\n",timeit.timeit('numpy.searchsorted(a,u)',sstr)
print "Simple replacement for binary search:takes the same args as np.searchsorted and similarly returns a new array. this performs only one trivial operation per element:\n",timeit.timeit('test.BSReplacement(a,u)',sstr)
print "barebones function doing nothing:",timeit.timeit('test.BareBones(a,u,r)',sstr)
print "Untyped inputs and doing N iterations:",timeit.timeit('test.UntypedWithLoop(a,u,r)',sstr)
print "time for just np.empty()",timeit.timeit('numpy.empty(10,int)',sstr)
二分查找執行需要執行的時間順序爲len(u)*Log(len(a))
。簡單的cython函數按照len(u)
的順序運行。兩者都返回一個len(u)的1D int數組。然而,即使這樣,在numpy庫中,沒有計算繁瑣的實現需要比全二分搜索更長的時間。 (這是寫在C:https://github.com/numpy/numpy/blob/202e78d607515e0390cffb1898e11807f117b36a/numpy/core/src/multiarray/item_selection.c看到PyArray_SearchSorted)
的結果是:
binary search: creates an array[N] and performs N binary searches to fill it:
1.15157485008
Simple replacement for binary search:takes the same args as np.searchsorted and similarly returns a new array. this performs only one trivial operation per element:
3.69442796707
barebones function doing nothing: 0.87496304512
Untyped inputs and doing N iterations: 0.244267940521
time for just np.empty() 1.0983929634
爲什麼np.empty()步走這麼多的時間?我能做些什麼來獲得我可以返回的空數組?
C函數執行此操作並運行一大堆理智檢查,並在內部循環中使用較長的算法。 (ⅰ去除除循環本身來回我的示例中,所有的邏輯)
更新
原來有兩個不同的問題:
- 的np.empty(10)單獨的呼叫具有巨大的開銷並且花費盡可能多的時間來進行搜索排序以創建新的數組並且執行10個二進制搜索
- 只聲明緩衝區語法
np.ndarray[...]
也有比接收無類型變量和迭代50次花費更多時間的大量開銷。
結果50次迭代:
binary search: 2.45336699486
Simple replacement:3.71126317978
barebones function doing nothing: 0.924916028976
Untyped inputs and doing N iterations: 0.316384077072
time for just np.empty() 1.04949498177
當你將'import'ed和'cimport'ed'numpy'命名爲相同時,它們通常會導入numpy爲np; cimport numpy as cnp'來區分它們。但我認爲在調用np.empty時'np'是'import'ed,並且沒有'cimport'ed,所以它是一個Python函數調用,它的開銷很大。你可以從Cython調用['PyArray_SimpleNew'](http://docs.scipy.org/doc/numpy/reference/c-api.array.html#PyArray_SimpleNew)來避免它,不知道如何。如果您擔心這種優化級別,請刪除Cython並一路轉到C-API ... – Jaime
@Jaime導入然後導入numpy,因爲np是標準用法,儘管混亂。我已經看到它完成你提到的方式,以及http://docs.cython.org/src/tutorial/numpy.html#adding-types我認爲這種方式的cython文檔建議可能重新綁定的cython變種相同名字時可用,就像發生在Python中的標準名稱空間衝突時發生的一樣 – JoshAdel
@JoshAdel我的觀點是,我不清楚調用'np.empty'是否正在進行Python函數調用,我認爲這會解釋頭頂上,或Cython的變種,這將表明在Cython的東西不是很好。但我曾經寫過的唯一的Cython就是'Hello World!'從文檔:我發現它很混亂,主要是因爲很難確定某些東西是在快速C還是慢Python中運行,並且一直移動到Python/NumPy C-API。所以我的意見是有偏見的,並不是很知情...... – Jaime