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

Профилирование памяти в Python: практическое руководство

6 min read Python Обновлено 17 Dec 2025
Профилирование памяти в Python: руководство
Профилирование памяти в Python: руководство

Python код и модули памяти

Зачем профилировать память

Понимание профиля памяти помогает не только находить утечки, но и выбирать более экономные структуры данных и алгоритмы. Это критично для: машинного обучения, ETL-процессов, long-running сервисов и видеопотоков, где пиковое потребление памяти определяет стоимость и надежность.

Ключевые понятия в одну строку:

  • Пик памяти — максимальное потребление в момент времени.
  • RSS (Resident Set Size) — физическая память процесса в ОС.
  • Инкремент строки — сколько дополнительно памяти выделяет конкретная строка кода.

Важное: профилирование всей программы может замедлить выполнение; сфокусируйтесь на подозрительных функциях или используйте выделённый модуль для профайлинга.

Инструменты и когда их использовать

  • memory_profiler — удобен для построчной аналитики и замеров функции. Хорош для разработки и локального анализа.
  • psutil — измеряет системную информацию процесса (RSS, VMS и т. п.). Подходит для оценки «всей» памяти процесса или интеграции в мониторинг.
  • tracemalloc — отслеживает аллокации памяти в самом Python (полезно для поиска утечек в рантайме Python-объектов).
  • pympler — набор инструментов для анализа объектов Python и их размеров.

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

pip install memory_profiler

Это также установит psutil как зависимость в большинстве случаев.

Быстрая оценка: размер объекта в памяти

Для первичной оценки, какой тип объекта легче, пригодится встроенная функция sys.getsizeof. Это полезно при выборе между list, tuple, dict и другими структурами.

Пример:

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

Примечание: sys.getsizeof возвращает размер самого объекта, но не суммирует память объектами, на которые он ссылается. Для оценки связанных объектов потребуется более глубокий анализ (pympler, objgraph, tracemalloc).

Сравнение функций в памяти

Можно сравнить объекты-функции (встроенная и пользовательская) по размеру объекта-функции:

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")

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

Сравнение размеров типов данных в памяти

Построчный профайл функции с memory_profiler

Чтобы получить построчную статистику памяти для функции, используйте декоратор @profile из memory_profiler:

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 — что позволяет увидеть, какие строки увеличивают память и на сколько.

Построчный вывод профайлера

Пояснение: колонка Increment отражает добавленную память, Occurrence — сколько раз происходило выделение/освобождение для этой строки.

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

mprof позволяет запускать скрипт и сэмплировать занятость памяти по времени. По умолчанию sampling ~0.1s.

Запуск:

mprof run script_name.py

Это создаст .dat файл с временным рядом потребления памяти.

Профиль памяти скрипта по времени

Если нужен график, установите matplotlib:

pip install matplotlib

Построить график:

mprof plot

График памяти во времени

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

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

import subprocess

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

# сохранить график локально
subprocess.run(['mprof', 'plot', '--output=output.jpg'])

Результат — файл output.jpg в рабочем каталоге.

Сохранённый график профиля скрипта

Измерение памяти функции по итогу выполнения с 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")

psutil измеряет RSS процесса — это удобно для интеграции в тесты и CI-пайплайны.

Общий вывод потребления памяти

Профилирование отдельных строк в Jupyter

В iPython/Jupyter можно применять магию %memit из memory_profiler для однострочных замеров (пик памяти и прирост):

  • В ячейке импортируете magics: ‘%load_ext memory_profiler’
  • В следующих ячейках используете ‘%memit код’ или ‘%%memit’ для многострочных ячеек.

Jupyter: код и вывод профайла памяти

Это удобно для интерактивного исследования и быстрого Q/A разработки.

Jupyter: профилирование функции

Практики оптимизации использования памяти

Ниже — короткие и проверенные эвристики по уменьшению потребления памяти:

  • Используйте генераторы и итераторы вместо списков там, где можно (генераторы потребляют память по требованию).
  • Обрабатывайте данные чанками (chunking), особенно для больших CSV/таблиц.
  • Для pandas: приводите типы столбцов (astype) к более узким типам, используйте category для строк с небольшим набором значений.
  • Избегайте создания большого количества временных копий DataFrame при цепочных операциях; применяйте inplace или собирайте операции в один вызов.
  • Для бинарных данных используйте array, memoryview или numpy, чтобы избежать оверхеда Python-объектов.
  • Для многопроцессности используйте shared memory или mmap, чтобы не дублировать большие объекты в каждом процессе.
  • Удаляйте ссылки на большие объекты (del) и, если нужно, вызывайте gc.collect() в контролируемых местах.
  • Профилируйте на данных, похожих на продакшн — маленькие тестовые наборы не выявят проблем с пиком памяти.

Mini-методология профайлинга памяти (шаги)

  1. Сформулируйте ожидаемое поведение и допустимые лимиты потребления (SLO по памяти).
  2. Локализуйте подозрительные области: ETL, загрузка файлов, предобработка, training loops.
  3. Быстрый замер: sys.getsizeof для типов; %memit в Jupyter для отдельных выражений.
  4. Построчный профиль: memory_profiler (@profile) для ключевых функций.
  5. Контекстный замер: psutil для оценки эффекта на весь процесс и для интеграционных тестов.
  6. Внедрение изменений + регресс-тесты по потреблению памяти.

Ролевые чек-листы

Разработчик:

  • Запустить post-line профилирование для новых функций с высоким потреблением данных.
  • Применить генераторы/итераторы при обработке потоков.
  • Добавить тест на потребление памяти в CI для критичных модулей.

Data Scientist / ML-инженер:

  • Сравнить dtype в датасете, выполнить downcast и проверить качество модели.
  • Использовать minibatch-обучение и offload (disk, memory-mapped files) для больших наборов.

DevOps / SRE:

  • Мониторить RSS и OOM-поведения в продакшне (коллекция метрик из psutil/экспорт в Prometheus).
  • Установить алерты на аномальный рост памяти и подготовить план автоматического перезапуска.

Когда методы не срабатывают или вводят в заблуждение (контрпримеры)

  • sys.getsizeof вернёт только «шероховатый» размер объекта; для вложенных объектов будьте осторожны.
  • memory_profiler на больших проектах добавляет оверхед и может изменить поведение по времени выполнения.
  • tracemalloc показывает аллокации Python, но не учитывает память, занятую объектами расширений на C (numpy, pandas внутренние буферы).
  • psutil измеряет весь процесс; если много других потоков/внешних библиотек, сложно привязать пик к конкретной функции.

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

  • tracemalloc — полезен для отладки утечек, сравнивает снапшоты аллокаций.
  • pympler — анализирует объекты в куче и их рост.
  • objgraph — визуализация ссылок между объектами для поиска циклических ссылок.

Критерии приёмки

  • Уменьшение пикового потребления памяти на X% (внутренние метрики можно определить проектно).
  • Отсутствие регрессивного роста памяти при нагрузочном тесте.
  • Автоматический тест, фиксирующий допустимый лимит памяти, проходит в CI.

Decision tree для выбора инструмента

flowchart TD
  A[Хочу профилировать память] --> B{Нужна ли построчная детализация?}
  B -- Да --> C[memory_profiler '@profile, %memit']
  B -- Нет --> D{Нужно ли измерять весь процесс?}
  D -- Да --> E[psutil]
  D -- Нет --> F{Ищете утечку или аллокации Python?}
  F -- Утечка --> G[tracemalloc]
  F -- Аллокации --> H[pympler / objgraph]
  C --> I[Анализ графиков mprof]
  E --> I
  G --> I
  H --> I

Тесты и приёмка (коротко)

  • Юнит-тест: функция не должна увеличивать RSS более допустимого лимита при обработке тестового набора.
  • Интеграционный тест: сценарий ETL не должен приводить к росту памяти в течение 30+ минут.
  • Нагрузочный тест: пиковое потребление должно быть в пределах аллокированных ресурсов контейнера.

Риски и рекомендации по смягчению

  • Оверхед профайлинга: не профилируйте в продакшне без ограничений — используйте sampling и короткие окна.
  • Неполная картина: комбинируйте инструменты (psutil + memory_profiler + tracemalloc) для разных уровней наблюдения.
  • False positives: профиль может показывать временные пики при нормальном поведении — сравнивайте с рабочими данными.

Важное: всегда измеряйте на реалистичных данных и сценариях.

Короткое резюме

Профилирование памяти — это сочетание инструментов (memory_profiler, psutil, tracemalloc) и практик (генераторы, downcast, chunking). Следуйте мини-методологии: измерили, локализовали, оптимизировали, протестировали. Включите контроль потребления памяти в CI и мониторинг — это снизит риск OOM в продакшне.

Ключевые шаги для старта: установить memory_profiler, замерить подозрительные функции с @profile, измерить эффект на процесс через psutil и применить оптимизации на основе полученных данных.

Важно: каждая среда и библиотека по‑разному управляют памятью; комбинируйте методы для полной картины.

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

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

Клавиатура автоматически выполняет сочетания — как исправить
Техподдержка

Клавиатура автоматически выполняет сочетания — как исправить

Батарея ноутбука застряла на 0% — как исправить
Ноутбуки

Батарея ноутбука застряла на 0% — как исправить

Тёмная тема в React с useState и useEffect
Frontend

Тёмная тема в React с useState и useEffect

Установка KB4474419 для Windows 7 — пошагово
Windows

Установка KB4474419 для Windows 7 — пошагово

Excel для резюме: готовим и настраиваем
Карьера

Excel для резюме: готовим и настраиваем

Как найти дешёвый бензин рядом
Авто

Как найти дешёвый бензин рядом