Профилировщик, который вы используете, будет запускать функцию, которую вы передаете memory_usage
несколько раз, так как он пытается определить интервал выборки памяти, что позволит ему получить достаточно выборок для точного считывания. Если ваш код выполняется слишком быстро, ему нужно будет уменьшить размер выборки, поэтому функция будет вызываться чаще.
Вы можете увидеть, как это работает, в исходном коде memory_profiler
. Вот важный цикл (с вырезанными ненужными вещами):
while True:
child_conn, parent_conn = Pipe() # this will store MemTimer's results
p = MemTimer(os.getpid(), interval, child_conn, backend,
timestamps=timestamps,
max_usage=max_usage,
include_children=include_children)
#...
returned = f(*args, **kw)
#...
n_measurements = parent_conn.recv()
#...
if n_measurements > 4 or interval < 1e-6:
break
interval /= 10.
Моя интерпретация вашего выходного графа заключается в том, что когда ваша функция вызывается с небольшим входом, interval
необходимо сжать три раза, прежде чем профилировщик получит достаточное разрешение для точного измерения использования памяти, поэтому количество вызовов для рекурсивная функция в четыре раза больше нормального уровня. После итерации 22 или около того интервал должен быть сокращен только дважды, поэтому значения графика изменяются в три раза по сравнению с нормальным уровнем. Есть также некоторый шум, хотя, где необычно быстрый или медленный запуск кода приводит к необычному количеству интервальных изменений, которые необходимо выполнить. Вот почему вы видите несколько долин, где требовалось меньше изменений, и одну вершину, где произошла дополнительная.
Вы можете избежать изменения размеров, если передадите соответствующий аргумент interval
в memory_usage
. По умолчанию используется значение 0.1
, поэтому вы, вероятно, можете избежать всех изменений, передав начальное значение 0.0001
(но при рассмотрении эксперимента вы можете сделать 0.0005
или более): memory_usage((sum_num, (i,)), 1e-4)