Профилировать memory fragmentation на GPU
ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Профилировать memory fragmentation на GPU
1. Цель задачи
Научиться программно измерять фрагментацию памяти CUDA в PyTorch с помощью torch.cuda.memory_stats(). Выявить источники фрагментации в сценарии работы мультиагентной LLM-системы (несколько моделей / копий агентов на одном GPU) и применить техники её снижения до уровня менее 10%.
Ключевой результат Запуск скрипта, который выводит процент фрагментации до и после оптимизации, и итоговое значение <10%.
2. Исходные данные
| Что нужно | Откуда взять |
|---|---|
| GPU с CUDA (не менее 4 ГБ памяти) | Локальная машина / облачная инстанция (AWS p3.2xlarge, Colab Pro) |
| PyTorch с CUDA (>=1.10) | Установка через pip / conda |
| Пример мультиагентной нагрузки | Тестовый скрипт (предоставлен ниже или написать самостоятельно) |
Утилита nvidia-smi или gpustat | Предустановлена или pip install gpustat |
| Базовые знания о memory pooling PyTorch | Документация: CUDA Memory Management |
Если нет реального GPU — симулируем:
- Использовать Google Colab (бесплатный T4) — создать ноутбук с GPU-рантаймом.
- Если Colab недоступен — использовать CPU-режим с эмуляцией аллокаций (заменить
cudaнаcpu, анализировать системную память черезpsutil, но фрагментация не будет точной). В этом случае задачу можно считать ознакомительной.
3. Технологический стек
| Компонент | Инструменты | Назначение |
|---|---|---|
| Язык программирования | Python 3.10+ | Скриптинг |
| Фреймворк ML | PyTorch (с CUDA) | Загрузка/инференс моделей, профилирование памяти |
| Профилирование CUDA | torch.cuda.memory_stats(), torch.cuda.memory_summary() | Сбор статистик фрагментации |
| Мониторинг GPU | nvidia-smi, gpustat | Контроль занятой памяти |
| Визуализация | matplotlib / pandas | График распределения блоков |
| Контроль памяти | torch.cuda.set_per_process_memory_fraction(), torch.cuda.empty_cache() | Ограничение и очистка |
4. Этапы выполнения
Этап 1: Подготовка окружения и тестовой нагрузки (30 минут)
Действия
-
Установить зависимости
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118 pip install gpustat matplotlib -
Проверить доступность GPU и версию PyTorch:
import torch print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0)) print(torch.version.cuda) -
Написать тестовый скрипт симуляции мультиагентной нагрузки:
- Создать 5 «агентов» — каждый загружает отдельную копию маленькой BERT-модели (
bert-base-uncased, 110M). - Агенты выполняют случайные forward-проходы с разным batch размером (1, 4, 8) и освобождают тензоры в случайные моменты времени.
- Цель: создать типичную фрагментацию (последовательные allocation/deallocation разного размера).
import torch import random import time def agent_work(model_id, device): model = torch.hub.load('huggingface/pytorch-transformers', 'model', 'bert-base-uncased').to(device) for _ in range(10): size = random.choice([1, 4, 8]) input_ids = torch.randint(0, 1000, (size, 128)).to(device) with torch.no_grad(): out = model(input_ids) time.sleep(0.05) del input_ids, out torch.cuda.empty_cache() del model - Создать 5 «агентов» — каждый загружает отдельную копию маленькой BERT-модели (
-
Запустить скрипт и убедиться, что память фрагментируется:
- Выполнить вызов
torch.cuda.memory_summary()после окончания работы агентов.
- Выполнить вызов
Ожидаемый результат этапа Рабочий скрипт, который воспроизводит фрагментацию памяти (обычно >20-40%).
Этап 2: Сбор профиля фрагментации (40 минут)
Действия
-
Получить словарь memory_stats
stats = torch.cuda.memory_stats(device=device) # Ключевые метрики: # 'num_alloc_retries' — количество retry-аллокаций (признак фрагментации) # 'segment.all.current' — число сегментов # 'reserved_bytes.all.current' — зарезервированная память # 'active_bytes.all.current' — активная (используемая) память -
Рассчитать процент фрагментации
Формула, рекомендованная PyTorch:reserved = stats['reserved_bytes.all.current'] active = stats['active_bytes.all.current'] fragmentation_pct = (1 - active / reserved) * 100 if reserved > 0 else 0Примечание: это приближённая оценка. Для точного анализа используют
memory_snapshot. -
Построить гистограмму размера свободных блоков
- Использовать
torch.cuda.memory_snapshot()— возвращает список блоков. - Для каждого сегмента: если статус
active_allocated— занят, иначе свободен. - Визуализировать размеры свободных блоков.
- Использовать
-
Замерить baseline
- Зафиксировать фрагментацию после тестовой нагрузки (до оптимизации).
Ожидаемый результат этапа Численное значение фрагментации в процентах, график распределения свободных блоков.
Этап 3: Анализ источника фрагментации (30 минут)
Действия
-
Выявить проблемные паттерны
-
Проверить влияние многомодельного инференса
- Каждая модель создаёт свой allocator cache.
- Используется ли
torch.cuda.set_per_process_memory_fraction()— ограничивает общий объём, но не решает фрагментацию.
-
Сформулировать гипотезу улучшения
Ожидаемый результат этапа Записанная гипотеза, выбранные методы оптимизации.
Этап 4: Оптимизация и снижение фрагментации (1 час)
Действия
-
Применить
expandable_segmentsexport PYTORCH_CUDA_ALLOC_CONF=expandable_segments:TrueИли через Python:
import os os.environ['PYTORCH_CUDA_ALLOC_CONF'] = 'expandable_segments:True' -
Оптимизировать нагрузку
- Вместо пяти копий модели — одна модель, inference батчами для всех агентов.
- Заранее выровнять размеры batch до степеней двойки (1, 2, 4, 8).
-
Добавить периодическую дефрагментацию
def defrag(threshold_pct=10): if fragmentation_pct > threshold_pct: torch.cuda.empty_cache() -
Снять повторные замеры
- Запустить изменённый скрипт, собрать
memory_statsснова.
- Запустить изменённый скрипт, собрать
Ожидаемый результат этапа Фрагментация стала ниже 10%.
Этап 5: Верификация и фиксация (20 минут)
Действия
-
Проверить воспроизводимость
- Запустить скрипт 3 раза, убедиться, что фрагментация стабильно <10%.
-
Проверить производительность
- Замерить время выполнения до и после. Оптимизация не должна увеличить latency более чем на 10%.
-
Написать финальный отчётный скрипт
- Выводит: дату, GPU, версию PyTorch, фрагментацию до/после, список применённых методов.
-
Задокументировать процедуру
- В README-файле или в комментариях внутри скрипта.
Ожидаемый результат этапа Готовый скрипт profile_fragmentation.py, который автоматически измеряет и снижает фрагментацию.
5. Критерии приемки (Definition of Done)
- Скрипт запускается на GPU-окружении без ошибок.
- Выводится
Fragmentation before: XX%,Fragmentation after: YY%. - Значение
afterстрого меньше 10%. - Разница по времени выполнения между baseline и оптимизацией не превышает 10%.
- Присутствует построенный график распределения свободных блоков (опционально — PNG-файл).
- Код содержит документацию строк (docstrings) и комментарии к ключевым шагам.
- Все зависимости зафиксированы в
requirements.txt.
6. Ожидаемый результат
- Основной артефакт Python-скрипт
profile_fragmentation.py(или Jupyter notebook), который: - Дополнительно График
fragmentation_hist.pngиrequirements.txt.
7. Возможные сложности и их решение
| Сложность | Решение |
|---|---|
| GPU-память быстро переполняется | Уменьшить количество моделей / batch size, использовать torch.cuda.set_per_process_memory_fraction(0.5) |
memory_stats() даёт ноль на некоторых версиях PyTorch | Убедиться, что после вызова torch.cuda.synchronize() статистики обновились |
expandable_segments недоступна (PyTorch <2.0) | Обновить PyTorch или использовать альтернативу: ручной вызов empty_cache + пул тензоров |
| После оптимизации вырос latency | Проверить, что empty_cache вызывается не слишком часто; добавить задержку по времени |
| Не удаётся добиться <10% | Комбинировать методы: expandable segments + batch inference + выравнивание размеров |
8. Бюджет времени (оценка)
| Этап | Время |
|---|---|
| Этап 1: Подготовка окружения и тестовой нагрузки | 30 мин |
| Этап 2: Сбор профиля фрагментации | 40 мин |
| Этап 3: Анализ источника фрагментации | 30 мин |
| Этап 4: Оптимизация и снижение фрагментации | 60 мин |
| Этап 5: Верификация и фиксация | 20 мин |
| Итого | 3 ч 00 мин |
При первом выполнении заложите 4 часа на освоение инструментов.
9. Связанные вопросы из базы знаний
| Вопрос | Тема |
|---|---|
| 45 | CUDA memory management basics |
| 67 | PyTorch Caching Allocator internals |
| 112 | How to reduce GPU memory fragmentation in multi-model serving |
| 158 | Expandable segments in PyTorch 2.0 |
| 234 | Batch inference vs per-request inference memory trade-offs |
| 389 | Monitoring GPU memory with nvidia-smi |
| 512 | Profiling LLM inference memory (kv-cache) |
| 678 | Debugging OOM in transformer models |
| 745 | Inter-agent communication overhead and memory sharing |
| 890 | Python memory profilers: memory_profiler vs tracemalloc |
10. Чек-лист самопроверки
- Я проверил, что GPU доступен и torch.cuda.is_available() возвращает True.
- Я запустил тестовый сценарий нагрузки и получил фрагментацию >10% (иначе задача не актуальна).
- Я корректно вычислил fragmentation по формуле
(1 - active/reserved)*100. - Я применил хотя бы два метода снижения фрагментации (expandable segments, batch inference, empty_cache).
- Я измерил время выполнения до и после и убедился, что оно не ухудшилось более чем на 10%.