Распределение памяти профилей в Python (с поддержкой массивов Numpy)
У меня есть программа, которая содержит большое количество объектов, многие из которых являются массивами Numpy. Моя программа меняет местами, и я пытаюсь уменьшить использование памяти, потому что на самом деле она не может работать в моей системе с текущими требованиями к памяти.
Я ищу хороший профилировщик, который позволит мне проверить объем памяти, потребляемый различными объектами (я представляю себе копию экземпляра памяти для cProfile), чтобы я знал, где оптимизировать.
Я слышал приличные вещи о Heapy, но Heapy, к сожалению, не поддерживает массивы Numpy, и большая часть моей программы включает массивы Numpy.
Ответы
Ответ 1
Один из способов решения проблемы, если вы вызываете множество разных функций, и вы не знаете, откуда происходит обмен, - использовать новую функцию построения графика из memory_profiler. Сначала вы должны украсить различные функции, которые вы используете с @profile. Для простоты я буду использовать пример examples/numpy_example.py, поставляемый с memory_profiler, который содержит две функции: create_data()
и process_data()
Чтобы запустить script, вместо того, чтобы запускать его с помощью интерпретатора Python, вы используете исполняемый файл mprof, то есть
$ mprof run examples/numpy_example.py
Это создаст файл с именем mprofile_??????????.dat
, где? будут содержать цифры, представляющие текущую дату. Чтобы построить результат, просто введите mprof plot
и он сгенерирует график, подобный этому (если у вас есть несколько файлов .dat, он всегда будет последним):
![output of memory_profiler's mprof]()
Здесь вы видите потребление памяти, с помощью скобок, указывающих, когда вы вводите/оставляете текущую функцию. Таким образом, легко видеть, что функция process_data()
имеет пик
потребления памяти. Чтобы продолжить работу в вашей функции, вы можете использовать профилировщик по очереди, чтобы видеть потребление памяти каждой строкой в вашей функции. Это выполняется с помощью
python -m memory_profiler examples/nump_example.py
Это даст вам результат, похожий на этот:
Line # Mem usage Increment Line Contents
================================================
13 @profile
14 223.414 MiB 0.000 MiB def process_data(data):
15 414.531 MiB 191.117 MiB data = np.concatenate(data)
16 614.621 MiB 200.090 MiB detrended = scipy.signal.detrend(data, axis=0)
17 614.621 MiB 0.000 MiB return detrended
где ясно, что scipy.signal.detrend выделяет огромный объем памяти.
Ответ 2
Посмотрите профайлер памяти. Он обеспечивает профилирование по линиям и интеграцию Ipython
, что упрощает ее использование:
In [1]: import numpy as np
In [2]: %memit np.zeros(1e7)
maximum of 3: 70.847656 MB per loop
Обновление
Как уже упоминалось @WickedGrey, при вызове функции более одного раза появляется ошибка (см. github issue tracker), которую я могу воспроизвести:
In [2]: for i in range(10):
...: %memit np.zeros(1e7)
...:
maximum of 1: 70.894531 MB per loop
maximum of 1: 70.894531 MB per loop
maximum of 1: 70.894531 MB per loop
maximum of 1: 70.894531 MB per loop
maximum of 1: 70.894531 MB per loop
maximum of 1: 70.894531 MB per loop
maximum of 1: 70.902344 MB per loop
maximum of 1: 70.902344 MB per loop
maximum of 1: 70.902344 MB per loop
maximum of 1: 70.902344 MB per loop
Однако я не знаю, на какие последствия могут повлиять результаты (кажется, это не так много в моем примере, поэтому в зависимости от вашего варианта использования это может быть полезно), и когда эта проблема может быть исправлена. Я спросил его в github.
Ответ 3
Так как numpy 1.7 существует полу-встроенный способ отслеживания выделения памяти:
https://github.com/numpy/numpy/tree/master/tools/allocation_tracking
Ответ 4
Можете ли вы просто сохранить или разжечь некоторые массивы на диск в файлах tmp, когда их не используете? Это то, что мне пришлось делать в прошлом с большими массивами. Конечно, это замедлит работу программы, но, по крайней мере, она закончится. Если вам не нужны все сразу?
Ответ 5
Вы пробовали valgrind
с помощью инструмента massif
?
valgrind --tool=massif python yourscript.py
он создаст файл с именем massif.out.xxx
, который вы можете проверить с помощью
ms_print massif.out.xxx | less
у него есть всякая полезная информация, но сюжет в самом начале должен быть тем, что вы ищете. Также ознакомьтесь с учебником массива на главной странице valgrind.
Использование valgrind
довольно продвинутое, и могут быть более простые способы делать то, что вы ищете.