Numba 3x медленнее, чем numpy

У нас есть векторная функция get_pos_neg_bitwise numpy, которая использует маску = [132 20 192] и df.shape (500e3, 4), которые мы хотим ускорить с помощью numba.

from numba import jit import numpy as np from time import time def get_pos_neg_bitwise(df, mask): """ In [1]: print mask [132 20 192] In [1]: print df [[ 1 162 97 41] [ 0 136 135 171] ..., [ 0 245 30 73]] """ check = (np.bitwise_and(mask, df[:, 1:]) == mask).all(axis=1) pos = (df[:, 0] == 1) & check neg = (df[:, 0] == 0) & check pos = np.nonzero(pos)[0] neg = np.nonzero(neg)[0] return (pos, neg) 

Используя советы от @morningsun, мы сделали эту версию numba:

 @jit(nopython=True) def numba_get_pos_neg_bitwise(df, mask): posneg = np.zeros((df.shape[0], 2)) for idx in range(df.shape[0]): vandmask = np.bitwise_and(df[idx, 1:], mask) # numba fail with # if np.all(vandmask == mask): vandm_equal_m = 1 for i, val in enumerate(vandmask): if val != mask[i]: vandm_equal_m = 0 break if vandm_equal_m == 1: if df[idx, 0] == 1: posneg[idx, 0] = 1 else: posneg[idx, 1] = 1 pos = list(np.nonzero(posneg[:, 0])[0]) neg = list(np.nonzero(posneg[:, 1])[0]) return (pos, neg) 

Но это все еще в 3 раза медленнее, чем numpy one (~ 0,06s Vs ~ 0,02s).

 if __name__ == '__main__': df = np.array(np.random.randint(256, size=(int(500e3), 4))) df[:, 0] = np.random.randint(2, size=(1, df.shape[0])) # set target to 0 or 1 mask = np.array([132, 20, 192]) start = time() pos, neg = get_pos_neg_bitwise(df, mask) msg = '==> pos, neg made; p={}, n={} in [{:.4} s] numpy' print msg.format(len(pos), len(neg), time() - start) start = time() msg = '==> pos, neg made; p={}, n={} in [{:.4} s] numba' pos, neg = numba_get_pos_neg_bitwise(df, mask) print msg.format(len(pos), len(neg), time() - start) start = time() pos, neg = numba_get_pos_neg_bitwise(df, mask) print msg.format(len(pos), len(neg), time() - start) 

Я что-то упускаю ?

 In [1]: %run numba_test2.py ==> pos, neg made; p=3852, n=3957 in [0.02306 s] numpy ==> pos, neg made; p=3852, n=3957 in [0.3492 s] numba ==> pos, neg made; p=3852, n=3957 in [0.06425 s] numba In [1]: 

One Solution collect form web for “Numba 3x медленнее, чем numpy”

Попробуйте перевести вызов на np.bitwise_and вне цикла, поскольку numba не может ничего сделать, чтобы ускорить его:

 @jit(nopython=True) def numba_get_pos_neg_bitwise(df, mask): posneg = np.zeros((df.shape[0], 2)) vandmask = np.bitwise_and(df[:, 1:], mask) for idx in range(df.shape[0]): # numba fail with # if np.all(vandmask == mask): vandm_equal_m = 1 for i, val in enumerate(vandmask[idx]): if val != mask[i]: vandm_equal_m = 0 break if vandm_equal_m == 1: if df[idx, 0] == 1: posneg[idx, 0] = 1 else: posneg[idx, 1] = 1 pos = np.nonzero(posneg[:, 0])[0] neg = np.nonzero(posneg[:, 1])[0] return (pos, neg) 

Затем я получаю тайминги:

 ==> pos, neg made; p=3920, n=4023 in [0.02352 s] numpy ==> pos, neg made; p=3920, n=4023 in [0.2896 s] numba ==> pos, neg made; p=3920, n=4023 in [0.01539 s] numba 

Итак, теперь numba немного быстрее, чем numpy.

Кроме того, это не имело большого значения, но в вашей исходной функции вы возвращаете массивы numpy, в то время как в версии numba вы конвертировали pos и neg в списки.

В общем, я бы предположил, что в вызовах функций преобладают функции numpy, которые numba не может ускорить, а numpy-версия кода уже использует быстрые подпрограммы векторизации.

Обновить:

Вы можете сделать это быстрее, удалив enumerate вызов и индекс непосредственно в массив вместо захвата среза. Также разделение pos и neg на отдельные массивы помогает избежать нарезки вдоль несмежной оси в памяти:

 @jit(nopython=True) def numba_get_pos_neg_bitwise(df, mask): pos = np.zeros(df.shape[0]) neg = np.zeros(df.shape[0]) vandmask = np.bitwise_and(df[:, 1:], mask) for idx in range(df.shape[0]): # numba fail with # if np.all(vandmask == mask): vandm_equal_m = 1 for i in xrange(vandmask.shape[1]): if vandmask[idx,i] != mask[i]: vandm_equal_m = 0 break if vandm_equal_m == 1: if df[idx, 0] == 1: pos[idx] = 1 else: neg[idx] = 1 pos = np.nonzero(pos)[0] neg = np.nonzero(neg)[0] return pos, neg 

И тайминги в ноутбуке ipython:

  %timeit pos1, neg1 = get_pos_neg_bitwise(df, mask) %timeit pos2, neg2 = numba_get_pos_neg_bitwise(df, mask)​ 100 loops, best of 3: 18.2 ms per loop 100 loops, best of 3: 7.89 ms per loop 
  • Python: как читать файл данных с нечетным количеством столбцов
  • Разница между np.int, np.int_, int и np.int_t в cython?
  • Обнаружение, если массив NumPy содержит хотя бы одно нечисловое значение?
  • Умножьте все столбцы в кадре данных Pandas вместе
  • Импорт звуковых файлов в Python в виде массивов NumPy (альтернативы аудиолабу)
  • Удаление специальных символов в рамке данных pandas
  • pandas создает новый столбец на основе значений из других столбцов
  • корректировка высоты отдельных подсетей в matplotlib в Python
  • Ненужный рулон в нескольких размерах
  • конвертировать список кортежей в структурированный массив numpy
  • Обтекание пользовательского типа указателем C ++ в Cython
  • Python - лучший язык программирования в мире.