2012-05-24 3 views
7

Мне нужно реализовать функцию для суммирования элементов массива с переменной длиной раздела. Так,Функция накопления cython numpy

a = np.arange(10) 
section_lengths = np.array([3, 2, 4]) 
out = accumulate(a, section_lengths) 
print out 
array([ 3., 7., 35.]) 

Я попытался реализация в cython здесь:

https://gist.github.com/2784725

для исполнения я по сравнению с чистым numpy решения для случая, когда section_lengths все равно:

LEN = 10000 
b = np.ones(LEN, dtype=np.int) * 2000 
a = np.arange(np.sum(b), dtype=np.double) 
out = np.zeros(LEN, dtype=np.double) 

%timeit np.sum(a.reshape(-1,2000), axis=1) 
10 loops, best of 3: 25.1 ms per loop 

%timeit accumulate.accumulate(a, b, out) 
10 loops, best of 3: 64.6 ms per loop 

у вас есть предложения по улучшению производительности?

+0

Я реализовал несколько предложений, см обновленную версию на GitHub: https://gist.github.com/2784725/8e2aaebbaa68c67e7a0686e9c7927f2f5b6f419a, до сих пор не принимает 63ms, поэтому никакого существенного улучшение –

+2

Возможно, это не так, но я подумал, что я бы сказал ... numpy уже имеет что-то близкое ему для * all * ufuncs. 'np.add.reduceat (a, section_lengths.cumsum())'. Его нужно немного изменить (cumsum не хватает 0 в начале, и вы получаете дополнительный фрагмент), и вы, вероятно, можете бить скорость с помощью cython, но это очень приятная функция/трюк. – seberg

ответ

2

Вы можете попробовать некоторые из следующих действий:

  • В дополнении к директиве @cython.boundscheck(False) компилятора, также попробуйте добавить @cython.wraparound(False)

  • В вашем setup.py сценарии, попробуйте добавить в некоторых флагах оптимизации:

    ext_modules = [Extension("accumulate", ["accumulate.pyx"], extra_compile_args=["-O3",])]

  • Посмотрите на файл .html, порожденного cython -a accumulate.pyx, чтобы увидеть, если есть участки, которые отсутствуют статическую типизацию или сильно опираясь на Python C-API вызовов:

    http://docs.cython.org/src/quickstart/cythonize.html#determining-where-to-add-types

  • Добавить return заявление в конце метода. В настоящее время он делает кучу ненужной проверки ошибок в вашем жестком цикле на i_el += 1.

  • Не уверен, если это будет делать различие, но я, как правило, делают счетчики цикла cdef unsigned int, а не просто int

Вы также можете сравнить ваш код Numpy когда section_lengths неравны, так как это, вероятно, потребует немного больше, чем просто простой sum.

+0

спасибо! Я выполнил все ваши предложения, но до сих пор нет значительного улучшения. Спасибо, что предложил cython -a, не знал об этом. Я добавил оператор возврата, который показывает некоторые странные проверки кода, см. Https: //gist.github.com/2784725 # gistcomment-330807 –

+0

Я принимаю этот ответ, потому что он дает полезные предложения, но ни один из них не дает значительного улучшения. Я изменю принятый ответ, если кто-нибудь еще найдет что-нибудь лучше. –

1

В гнезде для обновления цикла out[i_bas] работает медленно, вы можете создать временную переменную, чтобы сделать ее аккуратно, и обновить out[i_bas], когда закончен конец гнезда для цикла. Следующий код будет столь же быстро, как Numpy версии:

import numpy as np 
cimport numpy as np 

ctypedef np.int_t DTYPE_int_t 
ctypedef np.double_t DTYPE_double_t 

cimport cython 
@cython.boundscheck(False) 
@cython.wraparound(False) 
def accumulate(
     np.ndarray[DTYPE_double_t, ndim=1] a not None, 
     np.ndarray[DTYPE_int_t, ndim=1] section_lengths not None, 
     np.ndarray[DTYPE_double_t, ndim=1] out not None, 
     ): 
    cdef int i_el, i_bas, sec_length, lenout 
    cdef double tmp 
    lenout = out.shape[0] 
    i_el = 0 
    for i_bas in range(lenout): 
     tmp = 0 
     for sec_length in range(section_lengths[i_bas]): 
      tmp += a[i_el] 
      i_el+=1 
     out[i_bas] = tmp 
+0

спасибо! после вашего предложения, но нет существенного улучшения, я обновил свою версию на github –