Как скомпилировать jit-функцию numba с переменным типом ввода?

#python #random #signature #optional-parameters #numba

#python #Случайный #подпись #необязательно-параметры #numba

Вопрос:

Допустим, у меня есть функция, которая может принимать как int , так и None тип в качестве входного аргумента

 import numba as nb
import numpy as np

jitkw = {"nopython": True, "nogil": True, "error_model": "numpy", "fastmath": True}


@nb.jit("f8(i8)", **jitkw)
def get_random(seed=None):
    np.random.seed(None)
    out = np.random.normal()
    return out
  

Я хочу, чтобы функция просто возвращала нормально распределенное случайное число. Если я хочу воспроизводимые результаты, начальным значением должно быть int .

 get_random(42)
>>> 0.4967141530112327
get_random(42)
>>> 0.4967141530112327
get_random(42)
>>> 0.4967141530112327
  

Если я хочу случайные числа, seed их следует оставить как None . Однако, если я не передаю аргумент (поэтому по умолчанию используется значение seed None ) или явно передаю seed=None , то numba вызывает TypeError

 get_random()
>>> TypeError: No matching definition for argument type(s) omitted(default=None)
get_random(None)
>>> TypeError: No matching definition for argument type(s) omitted(default=None)
  

Как я могу написать функцию, все еще объявляя подпись и используя nopython режим для такого сценария?

Моя версия numba 0.43.1

Комментарии:

1. С numba это всегда полезно для дальнейшего использования и для тех, кто отвечает, если вы включаете свою версию numba. Это потому, что поддерживаемые функции могут сильно различаться в разных версиях.

Ответ №1:

Первая проблема заключается в том, что numba в режиме nopython принимает только (начиная с версии 0.43.1) np.random.seed : только с целочисленным аргументом.

Итак, к сожалению, вы не можете передать None .


Вторая проблема заключается в том, что (насколько я знаю) нет «единой» подписи, которая указывала бы numba, как обращаться с пропущенными значениями, однако вы можете использовать две подписи (да, это очень подробно):

 import numba as nb
import numpy as np

jitkw = {"nopython": True, "nogil": True, "error_model": "numpy", "fastmath": True}

@nb.jit(
    [nb.types.float64(nb.types.misc.Omitted(None)), 
     nb.types.float64(nb.types.int64)], 
    **jitkw)
def get_random(seed=None):
    return np.random.normal()
  

Просто краткое объяснение двух частей подписи:

  • nb.types.float64(nb.types.misc.Omitted(None)) Сообщает numba использовать None в качестве типа по умолчанию, если аргумент опущен
  • и nb.types.float64(nb.types.int64) это подпись, которая ожидает целое число.

Лично я бы не стал указывать подпись и просто позволил numba разобраться с этим. Явные подписи редко стоят того в numba, и чаще всего они приводят к более медленному и менее гибкому коду.

Комментарии:

1. Я испытал значительное ускорение при определении подписей массива numpy: т. Е. nb.float64[:] или nb.int64 [:,:] и т.д., Но не так много с обычными переменными.

2. @MottTheTuple Это интересно, потому что обычно вы не получаете ускорения с типами в numba — за исключением первого вызова, потому что, если вы не определяете типы, функция компилируется с заданными аргументами, в то время как с заданными типами функция компилируется, когда она определена. Цель подписи, по сути, «детализированный контроль над типами, выбранными компилятором» (из numba.pydata.org/numba-doc/latest/user /… ). Если у вас есть пример, где скорость функции действительно сильно отличается, мне было бы очень интересно привести пример (например, суть).

3. Где я видел это в моей функции линейной регрессии. Я отправляю некоторые экстремумы и вычисляю наклон, перехват и остатки с помощью незначительных числовых функций: сумма, среднее значение, макс и т.д. Я отбрасываю строки с наименьшими остатками. Все это выполняется в инструкции while, пока я не достигну минимального числа циклов в строке .1000 — время python: .0005, с nb.jit(nopython= True): .0019, nb.jit (nb.types.float64[:,:] (nb.types.float64[:,:], nb.int32), nopython=True) время: 1.5624e-05. Это одна из моих идей, может быть, не «массивная», но мне очень повезло с добавлением sigs в функции, где режим nopython сам по себе плохо работал.

4. @MottTheTuple В ваших таймингах вы исключили первый вызов (который включает стоимость компиляции)? Я немного знаю о внутренних компонентах numba, и есть несколько случаев, когда типизированные функции могут быть быстрее, в большинстве случаев они будут немного медленнее (за исключением случаев, когда вы также вводите, является ли массив непрерывным). Однако я был бы признателен, если бы вы могли создать gist (GitHub) или что-то еще, где я мог бы посмотреть.

5. Я вернулся к своим таймингам и обнаружил, что я НЕ исключил первый вызов. Я протестировал обе функции nopython, и обе работали примерно с одинаковой скоростью. Таким образом, ваша предпосылка, вероятно, верна, что подписи в большинстве случаев не нужны.. Возможно, у меня были специальные типы ввода в функции, которые я использовал раньше, что могло привести к ускорению, если я обнаружу, что я отправлю вам ссылку на суть. Спасибо за дружеское обсуждение.