Cython memoryview медленнее, чем ожидалось

Я начал использовать memoryviews в cython для доступа к массивам numpy. Одно из их различных преимуществ заключается в том, что они значительно быстрее, чем поддержка старого буфера numpy: http://docs.cython.org/src/userguide/memoryviews.html#comparison-to-the-old-buffer-support

Однако у меня есть пример, в котором поддержка старого буфера numpy работает быстрее, чем memoryviews! Как это может быть?! Интересно, правильно ли я использую 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: набранное число: 100000 циклов, лучшее из 3: 11,2 мкс за цикл

image_box2: memoryview: 100000 циклов, лучшее из 3: 18,1 мкс на цикл

Эти измерения выполняются из IPython с использованием% timeit image_box1 (im, pd, box_half_size)


person martinako    schedule 09.10.2012    source источник
comment
Я предполагаю, что вы синхронизируете эти функции с python? С тех пор возвращаемое значение - np.ndarray также во второй функции (я полагаю), что уже может объяснить замедление, поскольку создание np.ndarray - это небольшая дополнительная работа, а в целом здесь сделано не так много.   -  person seberg    schedule 09.10.2012
comment
Да, я рассчитал их из IPython с помощью команды:% timeit image_box1 (im, pd, box_half_size) Я только что отредактировал свой вопрос, чтобы включить время из cython. memoryviews все еще медленнее!   -  person martinako    schedule 09.10.2012
comment
исправление! вы правы, задержка в преобразовании из массива numpy в memoryview!   -  person martinako    schedule 09.10.2012


Ответы (1)


Хорошо! Я нашел проблему. Как отметил Себерг, 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: набранный номер: 9.07607864065e-05

image_box2: memoryview: 5.81799904467e-05

Так что memoryviews действительно быстрее!

Обратите внимание, что я преобразовал im и pd в memoryviews перед вызовом image_box2. Если я не сделаю этот шаг и передам im и pd напрямую, image_box2 будет работать медленнее:

image_box1: набранный номер: 9.12262257771e-05

image_box2: memoryview: 0.000185245087778

person martinako    schedule 09.10.2012
comment
Я не уверен, что это снова на 100% справедливо, поскольку, если вы наберете, вам, возможно, также следует ввести массивы в cython. Но я предполагаю, что в любом случае это сводится к некоторым преобразованиям (массив- ›memoryview или memoryview-› массив), но в любом случае это все крошечные накладные расходы. - person seberg; 09.10.2012
comment
Следуя вашему предложению, я попытался сделать тест более справедливым, скопировав im и pd в соответствующие типы буферов numpy. Я делаю это в тестовой функции, а затем передаю типы буфера в image_box1. Однако, если я попытаюсь передать эти типы буферов в image_box1, я получу ошибку компиляции. Типы буферов разрешены только как локальные переменные функции. Я пытался найти решение этой ошибки, но, похоже, не нашел его. Я начинаю думать, что это ограничение Cython. - person martinako; 10.10.2012