Производительность вложенной функции Numba

#python #numba

#python #numba

Вопрос:

в настоящее время я пытаюсь улучшить производительность моего кода на Python. Для этого я успешно использую numba. Для того, чтобы улучшить структуру моего кода, я создаю функции. Теперь я, к своему удивлению, заметил, что если я разделю код на разные функции numba, код будет работать значительно медленнее, чем если бы я поместил весь код в одну функцию с помощью декоратора numba. Примером может быть:

 @nb.njit
def fct_4(a, b):
    x = a ^ b
    setBits = 0
    while x > 0:
        setBits  = x amp; 1
        x >>= 1
    return setBits


@nb.njit
def fct_3(c, set_1, set_2):
    h = 2
    if c not in set_1 and c not in set_2:
        if fct_4(0, c) <= h:
            set_1.add(c)
        else:
            set_2.add(c)


@nb.njit
def fct_2(c, set_1, set_2):
    fct_3(c, set_1, set_2)


@nb.njit
def fct_1(set_1, set_2):
    for x1 in range(1000):
        c = 2
        fct_2(c, set_1, set_2)
  

медленнее, чем

 @nb.njit
def fct_1(set_1, set_2):
    for x1 in range(1000):
        c = 2       
        h = 2
        if c not in set_1 and c not in set_2:
            if fct_4(0, c) <= h:
                set_1.add(c)
            else:
                set_2.add(c)
  

с

 @nb.njit
def main_fct(set_1, set_2):
    for i in range(50):
        for x in range(1000):
            fct_1(set_1, set_2)

set_1 = {0}
set_2 = {47}

start = timeit.default_timer()
main_fct(set_1, set_2)
stop = timeit.default_timer()
  

(2,70 секунды против 0,46 секунды). Я думал, что это не должно иметь значения. Не могли бы вы просветить меня?

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

1. 1) Вы измеряете сочетание времени выполнения и времени компиляции. Запустите функцию один раз, прежде чем устанавливать время выполнения. 2) Серверная часть LLVM решает, встроить функцию или вызвать ее, если функции не встроены, ожидается разница. 3) Ваши функции действительно сложны для небольшого примера. Numba не может работать со списками или словарями напрямую, они преобразуются во внутреннее представление, что является очень дорогостоящим. Возможно, эти накладные расходы не оптимизированы для вложенных функций, что приводит к снижению производительности.

2. Могу ли я заставить LLVM наклонять функции? Будет ли подпись полезной? Должен ли я избегать использования наборов?

Ответ №1:

Поскольку python является динамически типизированным языком, накладные расходы на вызов его функции довольно высоки.

Кроме того, вы выполняете цикл по вызовам функций, поэтому время выполнения, затраченное на вызов функции и проверку аргументов, умножается в 1000 раз.

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

1. Но все функции изначально компилируются с помощью numba. Поэтому я предположил, что разницы быть не должно.

2. В противном случае, я думаю, мне следует просто избегать создания слишком большого количества отдельных функций.. Или есть способ повысить производительность путем добавления подписей?

3. Принимаете ли вы во внимание время компиляции? В вашем случае это также может сыграть свою роль в дополнительных временных затратах. Вы можете сохранить статические аргументы в Python версии 3.6, но я не думаю, что это настолько улучшит производительность. Однако для вашей программы я бы посоветовал вам взглянуть на общую картину — почему у вас несколько уровней зацикливания, нужно ли сохранять все таким модульным и т.д., и принять решение