Numba: когда использовать nopython=True?

У меня есть следующая установка:

import numpy as np
import matplotlib.pyplot as plt
import timeit
import numba
@numba.jit(nopython=True, cache=True)
def f(x):
    summ = 0
    for i in x:
        summ += i
    return summ

@numba.jit(nopython=True)
def g21(N, locs):
    rvs = np.random.normal(loc=locs, scale=locs, size=N)
    res = f(rvs)
    return res

@numba.jit(nopython=False)
def g22(N, locs):
    rvs = np.random.normal(loc=locs, scale=locs, size=N)
    res = f(rvs)
    return res

g22 и g21 — это одна и та же функция, только у одной из них есть nopython=True, а у другой nopython=False

Теперь я даю им ввод. Если locs является скаляром, то нумба должна быть в состоянии скомпилировать все, поскольку они поддерживают numpy.random.normal() с этой сигнатурой. Однако, если locs является массивом, numba не поддерживает эту подпись и должна вернуться к интерпретатору python.

Сначала я запускаю это, чтобы скомпилировать функции

N = 10_000

g22(N, 3)
g22(N, np.linspace(0,1,N))
g21(N, 3)
# g21(N, np.linspace(0,1,N))  # returns an error

Теперь я запускаю сравнение скорости

%timeit g21(N, 3)
%timeit g22(N, 3)
%timeit g22(N, np.linspace(0,1,N))

который возвращает

274 µs ± 3.43 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
270 µs ± 5.38 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
421 µs ± 54.3 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)

Имеет смысл, что g22(N, np.linspace(0,1,N) самый медленный, поскольку он возвращается к интерпретатору Python.

Однако я не понимаю, что g21(N, 3) примерно такая же скорость, как g22(N, 3), хотя у одного есть nopython=True, а у другого нет.

Но у g22(N,3) есть большое преимущество в том, что он может принимать другой аргумент, а именно g22(N, np.linspace(0,1,N)), поэтому он более универсален, однако в то же время нет штрафа за скорость при наличии nopython=False

Итак, мои вопросы:

  1. в этом случае какой смысл использовать nopython=True, если функция с nopython=False достигает той же скорости?

  2. в каком конкретном случае nopython=True лучше, чем nopython=False?

Не ответ на ваш вопрос, но, если это легко возможно, может иметь смысл просто реализовать функцию, используя поддерживаемые функции. например. res=0; for i in range(N):; res += np.random.normal(loc=locs[i], scale=locs[i]);. Для примеров, использующих случайные числа, также имеет смысл вызвать np.random.seed(0) в функции, чтобы получить те же значения.

max9111 18.03.2022 10:32

На самом деле это может быть полезно. Я не рассматривал это, поскольку по своей сути цикл for с генерацией RV кажется менее эффективным, чем генерация их всех сразу, однако при использовании numba может быть не так много накладных расходов, поэтому это стоит учитывать. Спасибо, я попробую это!

charelf 18.03.2022 11:12

Я тестирую это. Для 10 000 значений это примерно в два раза быстрее, чем реализация Numpy. Из-за более высоких статических накладных расходов разница становится больше, если количество значений уменьшается. Внутри Numpy, скорее всего, делает то же самое в скомпилированном коде. Не бойтесь циклов в скомпилированном коде;)

max9111 18.03.2022 11:24
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
3
51
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Обновлено: этот ответ неверен, см. комментарий ниже и вместо этого прочитайте принятый ответ


Ну, я продолжил использовать nopython=False, и кажется, что это вызывает немного больше компиляций. Это не очень научный анализ, но кажется, что моя функция иногда перекомпилируется, когда я изменяю различные параметры, тогда как с nopython=True она никогда не перекомпилировалась после компиляции. Так что, похоже, это разность потенциалов.

Насколько мне известно, на перекомпиляцию не влияет использование режима nopython. Однако обратите внимание, что вы можете указать подпись функции, чтобы с нетерпением ее строить. Для получения дополнительной информации об этом, пожалуйста, прочитайте: numba.readthedocs.io/en/stable/user/… . Также обратите внимание, что вы можете предоставить несколько подписей.

Jérôme Richard 17.03.2022 20:31

Вы правы, я вернулся к nopython=True и получил такое же количество перекомпиляций, так что это был ложный флаг. Спасибо за комментарий

charelf 17.03.2022 21:49
Ответ принят как подходящий
  1. in this case, what is the use of using nopython=True, if a function with nopython=False achieves same speed?
  2. in which specific case is nopython=True better than nopython=False?

документация указывает:

Numba has two compilation modes: nopython mode and object mode. The former produces much faster code, but has limitations that can force Numba to fall back to the latter. To prevent Numba from falling back, and instead raise an error, pass nopython=True.

Обратите внимание, что Numba попытается скомпилировать код в родной бинарник в оба режима. Однако nopython выдает ошибку, когда это невозможно, в то время как другой выдает предупреждение и вызывает использование резервного кода.

Для некоторых приложений производительность может быть критична, и поэтому вы действительно не хотите, чтобы вызывался резервный код. Например, это относится к высокопроизводительные приложения. В этом случае лучше иметь ошибку, чем иметь код, который работает несколько дней, а не несколько минут на дорогой машине (например, суперкомпьютере или вычислительном сервере). Использование другой версии Numba может привести к молча вызвать откат на некоторых машинах из-за неподдерживаемой функции. Я лично всегда использую режим nopython, чтобы предотвратить такие случаи (поскольку резервный код, как правило, слишком медленный, чтобы быть полезным), и я считаю объектный режим немного бесполезным. Дело короткое, nopython предлагает более сильные гарантии производительности.

Большое спасибо. Я не рассматривал аспект желания быть абсолютно уверенным, что код скомпилирован. В этом смысле действительно имеет смысл использовать nopython=True. Это имеет смысл.

charelf 17.03.2022 21:51

Другие вопросы по теме