Гид по технологиям

Как профилировать память в Python: инструменты, приёмы и шаблоны

7 min read Python Обновлено 13 Apr 2026
Профилирование памяти в Python
Профилирование памяти в Python

Кратко

Python предоставляет несколько способов измерить и оптимизировать использование памяти. В статье объяснено, когда применять sys.getsizeof, memory_profiler, psutil и mprof, приведены практические сценарии, пошаговые методики и чек-листы для разработчиков и SRE.

Введение

Python code with computer RAMs

Из-за принципа «есть более одного способа сделать это» в Python иногда трудно выбрать самый экономный по памяти подход. Профилировщик памяти помогает оценить потребление, найти утечки и определить, какие участки кода требуют оптимизации.

Это полезно как для задач машинного обучения с большими объёмами данных, так и для веб-приложений: можно оценивать память для скриптов, отдельных строк кода или функций.

Основная цель статьи

Показать практические инструменты и приёмы для оценки и улучшения использования оперативной памяти в Python-проектах. В тексте — инструменты, примеры кода, рекомендации по методике профилирования, чек-листы, сценарии отказа и альтернативы.

Некоторые инструменты для оценки профиля памяти в Python

Оценивать профиль памяти для всего кода целиком часто непрактично: профилирование замедляет приложение. Чаще профилируют выборочно — функции или методы, которые кажутся подозрительно «жадными» по памяти. При необходимости можно выделить отдельный модуль для профилирования.

Популярные библиотеки: memory_profiler, psutil, tracemalloc и pympler. В этом руководстве используются memory_profiler и psutil.

  • psutil — удобно измерять общее потребление процесса во время выполнения метода или функции.
  • memory_profiler — даёт подробную разбивку по строкам и позволяет видеть, какие строки увеличивают или уменьшают использование памяти.

Установка (в виртуальном окружении):

pip install memory_profiler

При установке memory_profiler часто подтягивается psutil.


Узнать размер объекта в памяти (sys.getsizeof)

В начале разработки полезно сравнивать размеры типов объектов, чтобы выбрать более лёгкий вариант для вашей задачи. Для этого используйте встроенную функцию sys.getsizeof — она возвращает размер самого объекта в байтах, но не учитывает объекты, на которые он ссылается.

Пример кода:

import sys
print(f"list size: {sys.getsizeof([])} bytes")
print(f"dictionary size: {sys.getsizeof(dict)} bytes")
print(f"tuple size: {sys.getsizeof(())} bytes")
print(f"set size: {sys.getsizeof({})} bytes")

Memory profile for data types

sys.getsizeof применим и для сравнения функций (объект функции тоже занимает место):

import sys

def getLength(iterable):
    count = 0
    for i in iterable:
        count +=1
    return count

print(f"Built-in length function: {sys.getsizeof(len)} bytes")
print(f"Custom length function: {sys.getsizeof(getLength)} bytes")

command line output memory comparison

Важно: sys.getsizeof возвращает размер только самого объекта. Если объект содержит ссылки на другие объекты (например, список с элементами), их объём в общий результат не включается. Для более полной картины используйте профайлеры, которые анализируют граф ссылок.

Подсказки: когда sys.getsizeof не подходит

  • Для контейнеров с вложенными объектами (список списков, объекты с атрибутами) — не отражает все вложенные объекты.
  • Для оценки оперативной памяти процесса в целом — нужен psutil или tracemalloc.

Профилирование функции по строкам с memory_profiler

memory_profiler даёт подробный отчёт по каждой строке функции при декорировании её профайлером @profile.

Пример (сохраните в файл и запустите через python, если используете декоратор @profile, требуется запуск через python -m memory_profiler или использование mprof):

import pandas
import numpy
from memory_profiler import profile

class Manipulate:
    @profile
    def manipulateData(self):
        df = pandas.DataFrame({
            'A' :[0, 3, numpy.nan, 10, 3, numpy.nan],    
            'B' : [numpy.nan, "Pandas", numpy.nan, "Pandas", "Python", "JavaScript"],   
        })

        df.fillna(method='bfill', inplace=True)
        df.fillna(method='ffill', inplace=True)
        return str(df)

manip = Manipulate()
print(manip.manipulateData())

В отчёте столбец Mem usage показывает значение памяти в конкретный момент, Increment — вклад строки, Occurrence — сколько раз строка выделяла или освобождала память.

Code output for line-by-line memory profiling

Совет по использованию: профилируйте небольшие фрагменты — иначе вывод будет громоздким.


Профилирование скрипта по времени: mprof

mprof (часть memory_profiler) запускает скрипт и снимает замеры в заданный интервал (по умолчанию 0.1 с). Команда:

mprof run script_name.py

После запуска создаётся файл .dat с выборками памяти. Вы можете построить график — для этого потребуется matplotlib:

pip install matplotlib
mprof plot

Memory profile of a Python script

График показывает профиль использования памяти по времени; ось X — время, справа отображены метки времени снятия замеров.


Запуск профилирования скрипта из отдельного Python-файла

Если вы хотите отделить профайлер от основной кодовой базы и сохранять графики локально:

import subprocess

subprocess.run([
    'mprof', 'run', '--include-children', 'missing.py'
])

# save the plot output locally
subprocess.run(['mprof', 'plot', '--output=output.jpg'])

Это полезно, когда нужно профилировать разнообразные скрипты автоматически, собирать результаты и архивировать графики.

Script memory profile plot


Измерить объём памяти, использованный во время выполнения функции (psutil)

psutil позволяет измерять реальное использование памяти процессом до и после выполнения функции.

import psutil
import sys
import os
sys.path.append(sys.path[0] + "/..")

# import the class containing your method
from somecode.missing import Manipulate

# instantiate the class
manip = Manipulate()

process = psutil.Process(os.getpid())
initial_memory = process.memory_info().rss

# run the target method:
manip.manipulateData()

# get the memory information after execution
final_memory = process.memory_info().rss
memory_consumed = final_memory - initial_memory
memory_consumed_mb = memory_consumed / (1024 * 1024)
print(f"Memory consumed by the function: {memory_consumed_mb:.2f} MB")

Этот подход оценивает совокупный прирост RSS процесса, связанный с выполнением метода.

Memory profile output total command line

Примечание: если внутри метода создаются побочные фоновые процессы или потоки, используйте параметр include_children для mprof или дополнительно измеряйте дочерние процессы в psutil.


Профилирование отдельной строки в Jupyter (магия %memit)

В Jupyter/IPython можно применять расширение memory_profiler и магию %memit для быстрого измерения пика памяти и увеличения объёма для одной строки:

  • Загрузите расширение в отдельной ячейке (например, %load_ext memory_profiler).
  • В последующих ячейках используйте %memit some_expression.

Этот способ удобен для интерактивного анализа, но не работает в обычных скриптах вне iPython.

Jupyter notebook code and output

Jupyter Notebook function memory profile


Практические рекомендации по повышению эффективности памяти в Python

Оптимизация памяти — это системная работа: как при проектировании алгоритма, так и при отладке:

  • Избегайте ненужных копий больших структур (np.copy, list(slice) и т. п.).
  • Используйте генераторы и итераторы вместо списков там, где возможно (range, generator expressions).
  • Для больших табличных данных рассматривайте dtype в pandas (категориальные колонки, downcasting чисел).
  • Освобождайте ресурсы явно (close, del, context managers) и контролируйте ссылки — циклические ссылки усложняют своевременную сборку мусора.
  • Для временных больших объектов используйте модуль gc для отладки (gc.collect(), gc.get_referrers).

Важно: не все «оптимизации ради памяти» оправданы — иногда стоимость CPU или читаемости важнее небольшой экономии RAM.


Методика профилирования: быстрый чек-лист (мини-SOP)

  1. Определите подозрительные участки: большие входные данные, долгие операции, всплески использования памяти в графиках.
  2. Локализуйте проблему: профилируйте конкретные функции по строкам (memory_profiler) или снимите общий график (mprof).
  3. Повторяйте тесты с различными размерами входных данных (малый, средний, большой).
  4. Сравните варианты реализации (например, list vs generator, object dtype vs category).
  5. Внедрите мониторинг в окружение (процесс-метрики) и установите SLI/SLO на использование памяти для production.

Чек-листы по ролям

Для разработчика

  • Профилируй локально функции с @profile.
  • Используй %memit для быстрых проверок в Jupyter.
  • Сравни варианты типов данных и структур.
  • Добавь unit-тесты, проверяющие, что peak memory не растёт при фиксированном объёме входа.

Для SRE/инженера по наблюдаемости

  • Мониторь RSS и OOM-кандидатов на проде.
  • Автоматически собирай mprof-дампы для долгоживущих инцидентов.
  • Настрой алерты по аномалиям использования памяти.

Шаблон критериев приёмки для оптимизации по памяти

  • Функция с тестовыми входными данными размера X использует максимум не более Y MB (график mprof показывает стабильную линию).
  • Внедрённый вариант не ухудшил время выполнения более чем на Z% (если затраты CPU критичны).
  • Нет регрессий в функциональности по существующим тестам.

(Задайте X, Y, Z согласно требованиям продукта и доступной инфраструктуре.)


Ментальные модели и эвристики

  • «Большой объект — подозрительно»: чаще всего проблема именно в крупных контейнерах.
  • «Копировать — дорого»: если вы создаёте копию данных при каждой итерации, это сигнал к рефакторингу.
  • «Профилировать реальное поведение»: синтетические микротесты полезны, но проблемы часто проявляются на продовых объёмах.

Когда предложенные инструменты не подойдут (контрпримеры)

  • Встроенные средства не покажут детальную картину в многопроцессном окружении без include-children.
  • Если код сильно асинхронен и использует C-расширения, memory_profiler может не увидеть выделения в C-слое — нужен tracemalloc или инструменты для нативной части.

Альтернативные подходы

  • tracemalloc — для отслеживания распределений памяти и стек-трейсов распределений (хорош для поиска «кто» выделил память).
  • pympler — для детального анализа объектов в памяти и графа ссылок.
  • Профайлеры нативного слоя (valgrind massif, tools для C/C++) — если проблема в C-расширениях.

Decision tree: как выбрать инструмент (Mermaid)

flowchart TD
  A[Нужно измерить память?] --> B{Проблема локальна или глобальна?}
  B -->|Локальна: одна функция/строка| C[memory_profiler / %memit]
  B -->|Глобальна: процесс/скрипт| D[psutil или mprof]
  D --> E{Есть C-расширения?}
  E -->|Да| F[tracemalloc или нативные инструменты]
  E -->|Нет| G[mprof + matplotlib]
  C --> H[Использовать @profile и анализировать increment]
  G --> I[Сохранить .dat, построить plot]

Факты и числа (коротко)

  • mprof по умолчанию снимает замеры каждые 0.1 секунды.
  • psutil предоставляет RSS (Resident Set Size) и другие метрики процесса.
  • sys.getsizeof измеряет размер объекта в байтах, не включая объекты по ссылке.

Тестовые сценарии и критерии приёмки

  • Тест 1: Малые данные — функция должна потреблять < 50 MB.
  • Тест 2: Увеличение данных в 10× не должно увеличивать память более чем пропорционально (проверка линейности).
  • Тест 3: Отсутствие утечки при многократных вызовах (память возвращается к базовому уровню).

(Конкретные пороговые значения задаёт команда в зависимости от окружения.)


Безопасность и приватность

При профилировании убедитесь, что логи и дампы памяти не содержат чувствительных данных (PII). Если профилирование собирается автоматически, примените маскирование и ограничьте доступ к результатам.


Глоссарий (1 строка каждая)

  • RSS — объём физической памяти, занимаемой процессом.
  • Heap — область памяти для динамически выделяемых объектов.
  • Garbage collector — механизм сборки неиспользуемых объектов.

Заключение и практические шаги

  • Начинайте с локального профилирования подозрительных функций (memory_profiler, %memit).
  • Для оценки эффекта на весь процесс используйте psutil и mprof.
  • Сравнивайте варианты реализации и проверяйте изменения на наборах данных разного размера.
  • Автоматизируйте сбор метрик и интегрируйте базовые SLI по памяти на проде.

Важно: оптимизация памяти — это компромисс между памятью, CPU и читаемостью кода. Оценивайте эффект комплексно.

Сводка

  • Профилируйте выборочно и по очереди: строка → функция → процесс.
  • memory_profiler и mprof хороши для анализа Python-кода; psutil — для измерения процесса.
  • sys.getsizeof полезен для быстрой оценки, но не отражает вложенные объекты.

Спасибо за внимание — используйте профиль памяти как часть стандартного цикла разработки и мониторинга.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Сетевой ресурс недоступен — исправление в Windows
Техподдержка

Сетевой ресурс недоступен — исправление в Windows

Как отложить SMS в Google Messages
Android.

Как отложить SMS в Google Messages

Адаптер Wi‑Fi не найден в Ubuntu — как исправить
Ubuntu

Адаптер Wi‑Fi не найден в Ubuntu — как исправить

Добавление и управление значками в Windows 11
Windows

Добавление и управление значками в Windows 11

Windows 11 не подключается к 5GHz Wi‑Fi — решения
Сеть

Windows 11 не подключается к 5GHz Wi‑Fi — решения

Создать ярлык приложения .desktop в Linux
Linux

Создать ярлык приложения .desktop в Linux