Сравните чистую функцию no-op Python с функцией no-op, украшенной @numba.jit
, то есть:
import numba
@numba.njit
def boring_numba():
pass
def call_numba(x):
for t in range(x):
boring_numba()
def boring_normal():
pass
def call_normal(x):
for t in range(x):
boring_normal()
Если это время с %timeit
, мы получаем следующее:
%timeit call_numba(int(1e7))
792 ms ± 5.51 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit call_normal(int(1e7))
737 ms ± 2.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Все совершенно разумно; есть небольшая накладная плата для функции numba, но не так много.
Если, однако, мы используем cProfile
для профайла этого кода, мы получаем следующее:
cProfile.run('call_numba(int(1e7)); call_normal(int(1e7))', sort='cumulative')
ncalls tottime percall cumtime percall filename:lineno(function)
76/1 0.003 0.000 8.670 8.670 {built-in method builtins.exec}
1 6.613 6.613 7.127 7.127 experiments.py:10(call_numba)
1 1.111 1.111 1.543 1.543 experiments.py:17(call_normal)
10000000 0.432 0.000 0.432 0.000 experiments.py:14(boring_normal)
10000000 0.428 0.000 0.428 0.000 experiments.py:6(boring_numba)
1 0.000 0.000 0.086 0.086 dispatcher.py:72(compile)
cProfile
считает, что при вызове функции numba существует огромная накладная стоимость. Это распространяется на "настоящий" код: у меня была функция, которая просто называлась моим дорогостоящим вычислением (вычисление было numba-JIT-скомпилировано), а cProfile
сообщил, что функция-обертка занимает около трети от общего времени.
Я не возражаю против того, что cProfile
добавляет немного накладных расходов, но если он массово несовместим с тем, где он добавляет, что накладные расходы это не очень полезно. Кто-нибудь знает, почему это происходит, есть ли что-нибудь, что можно сделать по этому поводу, и/или если есть какие-либо альтернативные инструменты профилирования, которые плохо взаимодействуют с numba?