2012-10-09 33 views
4

我已經開始在cython中使用memoryviews來訪問numpy數組。他們具有的各種優點之一是它們比舊的numpy緩衝區支持要快得多: http://docs.cython.org/src/userguide/memoryviews.html#comparison-to-the-old-buffer-supportcython memoryview比預期慢

但是,我有一個例子,其中舊的numpy緩衝區支持比memoryviews更快! 這怎麼可能?我想知道我是否正確使用了記憶體?

這是我的測試:

import numpy as np 
cimport numpy as np 
cimport cython 

@cython.boundscheck(False) 
@cython.wraparound(False) 
cpdef np.ndarray[np.uint8_t, ndim=2] image_box1(np.ndarray[np.uint8_t, ndim=2] im, 
               np.ndarray[np.float64_t, ndim=1] pd, 
               int box_half_size): 
    cdef unsigned int p0 = <int>(pd[0] + 0.5) 
    cdef unsigned int p1 = <int>(pd[1] + 0.5)  
    cdef unsigned int top = p1 - box_half_size 
    cdef unsigned int left = p0 - box_half_size 
    cdef unsigned int bottom = p1 + box_half_size 
    cdef unsigned int right = p0 + box_half_size  
    cdef np.ndarray[np.uint8_t, ndim=2] box = im[top:bottom, left:right] 
    return box 

@cython.boundscheck(False) 
@cython.wraparound(False) 
cpdef np.uint8_t[:, ::1] image_box2(np.uint8_t[:, ::1] im, 
            np.float64_t[:] pd, 
            int box_half_size): 

    cdef unsigned int p0 = <int>(pd[0] + 0.5) 
    cdef unsigned int p1 = <int>(pd[1] + 0.5)  
    cdef unsigned int top = p1 - box_half_size 
    cdef unsigned int left = p0 - box_half_size 
    cdef unsigned int bottom = p1 + box_half_size 
    cdef unsigned int right = p0 + box_half_size  
    cdef np.uint8_t[:, ::1] box = im[top:bottom, left:right] 
    return box 

時機結果是:

image_box1:輸入numpy的: 100000循環,最好的3:11.2%環我們

image_box2:memoryview: 100000循環,最好的3:每循環18.1美元

這些測量是從IPython使用%timeit完成image_bo x1(im,pd,box_half_size)

+1

我猜你是從python計時這些功能?從那時起,返回值就是第二個函數中的'np.ndarray'(我假設),這可能已經解釋了減速,因爲使得'np.ndarray'有點額外的工作,這裏沒有太多的工作要做總體。 – seberg

+0

是的,我從IPython使用以下命令對它們進行了計時: %timeit image_box1(im,pd,box_half_size) 我剛剛編輯了我的問題以包含cython內的時間。記憶體觀點仍然較慢! – martinako

+0

更正!你是對的,延遲是從numpy數組轉換到memoryview! – martinako

回答

2

好吧!我發現了這個問題。 正如seberg指出,memoryviews顯得比較慢,因爲測量包括從numpy數組到memoryview的自動轉換。

我用下面的函數來從用Cython模塊內測量次數:

def test(params): 
    import timeit 
    im = params[0] 
    pd = params[1] 
    box_half_size = params[2] 
    t1 = timeit.Timer(lambda: image_box1(im, pd, box_half_size)) 
    print 'image_box1: typed numpy:' 
    print min(t1.repeat(3, 10)) 
    cdef np.uint8_t[:, ::1] im2 = im 
    cdef np.float64_t[:] pd2 = pd 
    t2 = timeit.Timer(lambda: image_box2(im2, pd2, box_half_size)) 
    print 'image_box2: memoryview:' 
    print min(t2.repeat(3, 10)) 

結果:

image_box1:輸入numpy的: 9.07607864065e-05

image_box2:memoryview: 5.81799904467e-05

因此,記憶體確實更快!

請注意,我在調用image_box2之前將im和pd轉換爲內存視圖。如果我不做這一步,我通過IM和PD直接,然後image_box2較慢:

image_box1:輸入numpy的: 9.12262257771e-05

image_box2:memoryview: 0.000185245087778

+0

我不確定這是否再次100%公平,因爲如果你鍵入,你應該也可以在cython中鍵入數組。但我想無論如何它歸結爲一些轉換(array-> memoryview或memoryview-> array),但這無論如何都是微小的開銷。 – seberg

+0

遵循你的建議,我試圖通過將im和pd複製到相應的numpy緩衝區類型來使測試更公平。我在測試函數中執行此操作,然後將緩衝區類型傳遞給image_box1。但是,如果我嘗試將這些緩衝區類型傳遞給image_box1,我得到編譯錯誤「緩衝區類型只允許作爲函數局部變量」 我一直在試圖找到這個錯誤的解決方案,但我似乎沒有找到任何。我開始認爲這是一個cython限制。 – martinako