Функция автоджита Numba медленнее, чем векторизованный метод Numpy

У меня есть следующий цикл for для построения списка значений:

p = 7
A = []

for i in range(0, 10**p):
    A.append(i**3 + i**2)

Чтобы ускорить создание списка, я создал его в виде массива Numpy, используя векторизованный подход. Этот подход намного быстрее, чем эквивалентный цикл for, особенно для больших значений p, что увеличивает диапазон.

import numpy as np
from numba import autojit

p = 7
m = np.arange(0, 10**p)
D = np.empty(len(m))
D = m**3 + m**2

Чтобы еще больше ускорить создание массива, я решил попробовать использовать пакет Numba. Ниже моя текущая попытка.

@autojit
def func(a):
    a = np.asarray(a)
    n = np.arange(0, 10**p)
    a = np.append(a, n**3 + n**2)
    return a

e = []
E = func(e)

К сожалению, я не вижу никакого прироста производительности от использования Numba, который почти в 3 раза медленнее, чем векторизованный подход, использующий только Numpy.

Любые предложения о том, как использовать Numba для этого?


person wigging    schedule 14.10.2014    source источник
comment
функция numpy уже векторизована, использование @autojit будет иметь огромное значение в подходе с циклом for   -  person Saullo G. P. Castro    schedule 14.10.2014
comment
@SaulloCastro Итак, вы говорите, что оберните for в функцию с autojit? Можете ли вы отправить ответ с таким подходом и сравнить время?   -  person wigging    schedule 15.10.2014
comment
мой ответ был бы похож на ответ Джоша Адельса, смысл в том, чтобы писать как на Фортране при использовании Numba, просто напомнив, что индексы здесь начинаются с 0 :)   -  person Saullo G. P. Castro    schedule 15.10.2014


Ответы (1)


Numba не делает вызовы произвольных методов быстрее. Если вы обращаетесь к библиотеке, numba в большинстве случаев ничего не может с этим поделать. Но если вы перепишете вещи немного по-другому, вы все равно можете получить приличное ускорение (я использую numba 0.14.0 — если вы используете другую версию, оборудование и т. д., вы можете получить другие результаты, тем более что numba находится в активной разработке):

import numpy as np
import numba as nb

def func(a, p):
    a = np.asarray(a)
    n = np.arange(0, 10**p)
    a = np.append(a, n**3 + n**2)
    return a

@nb.jit
def func2(a, p):
    a = np.asarray(a)
    n = np.empty(10**p, dtype=np.float64)
    for k in range(10**p):
        n[k] = k*k*(k + 1)

    return np.append(a, n)

p = 6
e = []
E = func(e, p)
E2 = func2(e, p)
print np.allclose(E, E2)

И тайминги:

In [51]:

%timeit func(e, p)
10 loops, best of 3: 42.9 ms per loop
In [52]:

%timeit func2(e, p)
100 loops, best of 3: 3.09 ms per loop

Также с p=7 вам нужно быть немного осторожным с числовой точностью.

Ключ с numba заключается в развертывании циклов и выполнении только «примитивных» арифметических вызовов, которые поддерживает numba.

person JoshAdel    schedule 14.10.2014
comment
Увеличение скорости в вашем func2 связано с k*k*(k+1), а не обязательно с использованием nb.jit. Если вы измените n**3+n**2 на n*n*(n+1) в func, то это все равно будет быстрее, чем подход numba. - person wigging; 15.10.2014
comment
На моей машине n*n*(n+1) по-прежнему в 2 раза медленнее, чем numba. На самом деле я немного удивлен этим, так как я думал, что будет создан аналогичный уровень временных массивов, но я предполагаю, что путь кода для оператора ** должен быть немного другим. - person JoshAdel; 15.10.2014
comment
Может быть, что-то еще не так с моим кодом. Вот файл, который я запускаю github.com/wigging/python_examples/blob/ master/vectorize.py - person wigging; 15.10.2014
comment
@ Гэвин, я предполагаю, что вы синхронизируете функцию numba только один раз, поэтому время включает в себя jit-код кода, тогда как, когда я использую timeit, я получаю лучшее время от теплого старта. - person JoshAdel; 15.10.2014
comment
Ах-ха, ты прав! Время, которое я получаю, это от холодного запуска. Используя функцию timeit, я получаю подход Numba как более быстрый. Так есть ли способ сохранить jit-код? Например: при запуске нового экземпляра Python я просто запускал уже скомпилированный код вместо того, чтобы проходить процесс прогрева. - person wigging; 15.10.2014
comment
Я думаю, что сохранение jit-кода включено в дорожную карту для numba, но в настоящее время недоступно. - person JoshAdel; 15.10.2014
comment
Облом, было бы неплохо иметь возможность сохранять отредактированный код. Я могу отправить еще один вопрос о переполнении стека, чтобы повысить осведомленность о такой функции. Спасибо за помощь! - person wigging; 15.10.2014
comment
В самой новой версии, похоже, есть флаг «кэш» для @jit, это версия 0.22. Будет очень интересно, когда numba перестанет быть такой движущейся мишенью... - person webb; 02.11.2015
comment
Я использовал флаг cache, и он работает хорошо и станет еще лучше между 0,21 и 0,22. - person JoshAdel; 02.11.2015