2015-06-19 6 views
3

Следующий код имитирует извлечение двоичных слов из разных мест в пределах набора изображений.Функция Numba медленнее, чем C++ и повторный порядок циклов далее замедляется x10

Numba обернут функцию, wordcalc в коде ниже, имеет 2 проблемы:

  1. Это в 3 раза медленнее по сравнению с аналогичной реализации в C++.
  2. Как ни странно, если вы переключаете порядок «ibase» и «ibit» для петель, скорость падает в 10 раз (!). Это не происходит в реализации C++, которая остается незатронутой.

Я использую Numba 0.18-2 из WinPython 2.7

Что может быть причиной этого?

imDim = 80 
numInsts = 10**4 
numInstsSub = 10**4/4 
bitsNum = 13; 

Xs = np.random.rand(numInsts, imDim**2)  
iInstInds = np.array(range(numInsts)[::4]) 
baseInds = np.arange(imDim**2 - imDim*20 + 1) 
ofst1 = np.random.randint(0, imDim*20, bitsNum) 
ofst2 = np.random.randint(0, imDim*20, bitsNum) 

@nb.jit(nopython=True) 
def wordcalc(Xs, iInstInds, baseInds, ofst, bitsNum, newXz): 
    count = 0 
    for i in iInstInds: 
     Xi = Xs[i]   
     for ibit in range(bitsNum): 
      for ibase in range(baseInds.shape[0]):      
       u = Xi[baseInds[ibase] + ofst[0, ibit]] > Xi[baseInds[ibase] + ofst[1, ibit]] 
       newXz[count, ibase] = newXz[count, ibase] | np.uint16(u * (2**ibit)) 
     count += 1 
    return newXz 

ret = wordcalc(Xs, iInstInds, baseInds, np.array([ofst1, ofst2]), bitsNum, np.zeros((iInstInds.size, baseInds.size), dtype=np.uint16)) 
+0

Я предполагаю, что разница в производительности при переключении порядка цикла имеет какое-то отношение к кэш-памяти. –

+0

@LakshayGarg Я думал то же самое, но реализация на C++ не чувствительна к этому вообще. – Leo

+0

Очень маловероятно, но, возможно, компилятор достаточно умен, чтобы оптимизировать это для вас. Какой компилятор вы используете? –

ответ

3

я получаю 4x ускорение при переходе от np.uint16(u * (2**ibit)) к np.uint16(u << ibit); т. е. заменить мощность 2 на бит-сдвиг, который должен быть эквивалентным (для целых чисел).

Похоже, что ваш C++-компилятор может сделать эту замену сам.

Перестановка порядка двух петель делает для меня небольшую разницу как для исходной версии (5%), так и для моей оптимизированной версии (15%), поэтому я не могу думать, что могу сделать полезный комментарий.

Если вы действительно хотели сравнить Numba и C++, вы можете посмотреть скомпилированную функцию Numba, выполнив os.environ['NUMBA_DUMP_ASSEMBLY']='1' перед тем, как импортировать Numba. (Тем не менее, это явно связано с этим).

Для справки, я использую Numba 0.19.1.

+1

Спасибо, это значительно сократило разрыв. C++ теперь на 30% быстрее. Обратите внимание, что: np.uint16 (u << ibit) на самом деле имеет ошибку. Numba считает, что «u» как int8 по-видимому и так, например, u << 10 всегда приводит к 0. Причинение функции вести себя иначе, чем чистая версия python. Мне пришлось исправить это: np.uint16 (u + 0) << ibit – Leo

+0

@DavidW, @Leo: Эта ошибка является известной проблемой? Было бы здорово, если бы вы могли решить проблему в Github. В любом случае, я думаю, что ответ должен включать 'np.uint16 (u + 0) << ibit' – cd98

+0

Извините, я вижу, что это сообщается в [номер 1241] (https://github.com/numba/numba/ вопросы/1241) – cd98