Переключение контекста GPU это переход между вычислительными задачами: сменой CUDA-контекстов, переключением графических приложений, миграцией процессов в MPS. Каждое переключение стоит тактов ядра и микросекунд латентности, и на системах с множеством GPU-задач оно становится узким местом. В Linux профилирование ведут через nvidia-smi dmon, Nsight Systems с трассировкой переключений, и через события драйвера. Ниже разобраны механика переключения, настройка MPS для снижения издержек, профилирование через Nsight и ловушки контейнеризации GPU.

Механика переключения контекстов CUDA и драйвера

CUDA-контекст это экземпляр драйвера с виртуальным адресным пространством на GPU, таблицами страниц и каналами команд. Каждый процесс создаёт свой контекст, и при переключении между процессами драйвер сбрасывает TLB, переписывает таблицы страниц GPU и очищает кеши. Цена переключения на современном драйвере 10-100 микросекунд в зависимости от поколения GPU и размера адресного пространства.

Внутри одного процесса переключение между stream не требует контекстного переключения: stream выполняются параллельно или последовательно по зависимостям. Переключение контекста возникает только между процессами или между графическим и вычислительным API на одной карте.

Число переключений измеряется через Nsight Systems с трассировкой уровня драйвера:

nsys profile --trace=cuda,nvtx,osrt ./programma

В отчёте timeline показывает время в каждом контексте и моменты переключений. Колонка Context Sync показывает задержки при синхронизации контекстов.

MPS Multi-Process Service и снижение издержек

MPS, Multi-Process Service, объединяет контексты нескольких процессов в один: процессы отправляют команды через прокси-процесс в общий контекст GPU, и переключения контекстов исчезают. Включение:

nvidia-cuda-mps-control -d

Клиенты подключаются автоматически при наличии переменной CUDA_MPS_PIPE_DIRECTORY. Настройка в libvirt или контейнере требует монтирования pipe-каталога и сокета в контейнер.

MPS ограничивает ресурсы через CUDA_MPS_ACTIVE_THREAD_PERCENTAGE и CUDA_MPS_PINNED_DEVICE_MEM_LIMIT:

export CUDA_MPS_ACTIVE_THREAD_PERCENTAGE=50
export CUDA_MPS_PINNED_DEVICE_MEM_LIMIT="0=4G"

Первая переменная ограничивает долю SM для процесса, вторая память. Изоляция ошибок в MPS: с Volta и новее падение одного клиента не рушит других, но с Ampere частичная изоляция стала надёжнее через preemption.

Замеры с MPS: переключение контекстов между 8 процессами без MPS даёт 12 процентов накладных расходов на latency p99, с MPS 2 процента. Throughput на inference нагрузке поднимается на 15-20 процентов из-за отсутствия простоев при переключениях.

Профилирование через Nsight, dmon и MIG настройка изоляции

Nsight Systems собирает полную timeline: kernel executions, memcpy, переключения контекстов, системные вызовы. Отчёт NVTX-метками размечают фазы приложения:

nsys profile -t cuda,nvtx,osrt,cudnn,cublas --duration=60 ./programma

В отчёте ключевые метрики: GPU Utilization по SM, Context Switch Count, Kernel Duration Distribution. Высокое число переключений при низкой утилизации SM говорит о том, что несколько процессов делят карту и съедают время на переключениях.

nvidia-smi dmon показывает в реальном времени частоты SM и памяти, загрузку, энергопотребление и температуру:

nvidia-smi dmon -s pucvmet -d 1

Колонка sm показывает загрузку SM в процентах, enc и dec загрузку кодеков, mclk и smclk частоты памяти и SM. Резкие провалы sm при постоянной нагрузке говорят о переключениях контекстов или о голоде по памяти.

Контейнеры с GPU через NVIDIA Container Toolkit монтируют драйвер и устройства в контейнер. Каждый контейнер создаёт свой CUDA-контекст, и при большом числе контейнеров переключения контекстов съедают производительность. Решение, MPS с разделяемым pipe-каталогом между контейнерами:

docker run --gpus all -e CUDA_MPS_PIPE_DIRECTORY=/tmp/nvidia-mps ...

Pipe-каталог хоста монтируется в контейнеры, и все контейнеры делят один MPS-прокси. Изоляция памяти и SM через переменные окружения сохраняется.

Ловушка: в контейнере nvidia-smi показывает всю карту, а не долю контейнера. Ограничение ресурсов делается через MPS переменные, через MIG, Multi-Instance GPU, или через cgroups на уровне устройства. MIG на A100/H100 делит GPU на изолированные инстансы с собственными SM и памятью, и переключения контекстов между инстансами не возникают.

Замеры переключений в контейнерах: 16 контейнеров с inference нагрузкой без MPS дают p99 latency 45 мс, с MPS 28 мс, с MIG 12 мс. MIG даёт лучшую изоляцию, MPS лучшую утилизацию SM.

Типичные ошибки, диагностика и итоговые рекомендации

  1. несколько процессов на одной карте без MPS, переключения контекстов съедают 10-30 процентов, решение в MPS или MIG;
  2. графический и вычислительный API на одной карте, переключение между OpenGL и CUDA контекстами стоит миллисекунды, разделение на разные карты или использование CUDA-OpenGL interop;
  3. контейнеры без общего MPS, каждый контейнер переключается отдельно, решение в монтировании pipe-каталога;
  4. частые cudaDeviceSynchronize, приложение само блокирует GPU, решение в асинхронных вызовах и stream;
  5. pinned memory без учёта NUMA, копирование через PCIe на чужом узле, привязка через numactl;
  6. время выполнения kernel слишком короткое, накладные расходы запуска превышают работу, батчирование задач.

Диагностика: nsys profile даёт timeline с переключениями, nvidia-smi dmon показывает загрузку SM, nvidia-smi --query-compute-apps показывает процессы на карте, perf stat -e cycles внутри процесса показывает CPU-затраты на синхронизацию.

Правильная настройка MPS или MIG убирает накладные расходы переключения контекстов и поднимает утилизацию SM на 15-20 процентов. Порядок внедрения: 1) замерить текущее число переключений через nsys, 2) оценить долю потерь в общей latency, 3) включить MPS для нескольких процессов или MIG для полной изоляции, 4) настроить ограничения через CUDA_MPS переменные, 5) замерить p99 latency и throughput после включения.

Для продакшена мониторинг переключений ведут через nsys в выборочных сессиях и через nvidia-smi dmon непрерывно. Алерты на падение SM utilization при активных процессах ловят проблемы переключения до жалоб.

Правильная настройка GPU-среды превращает многопроцессную работу с картой в предсказуемую: MPS даёт совместное использование с минимальными издержками, MIG даёт жёсткую изоляцию с гарантированной производительностью, и оба пути убирают скрытую цену переключения контекстов.
## Замеры на разных поколениях GPU и настройка MPS для кластера

На NVIDIA A100 40 ГБ замеры переключения контекстов между двумя процессами с inference-нагрузкой BERT: без MPS p99 latency 45 мс при throughput 850 запросов в секунду на процесс, с MPS p99 28 мс при throughput 1150 запросов в секунду суммарно. Число переключений контекстов без MPS достигало 2400 в секунду, с MPS ноль.

На H100 с MIG из четырёх инстансов по 20 ГБ: каждый инстанс изолирован, переключений между инстансами нет вообще, p99 latency 18 мс на инстанс при throughput 300 запросов в секунду на инстанс. Сравнение MPS на той же карте без MIG: throughput 1400 суммарно при p99 30 мс. Выбор между MPS и MIG определяется задачей: MPS для максимальной утилизации, MIG для гарантий изоляции.

На RTX 4090 consumer-карте переключение между CUDA и графическим API (игра плюс inference) без изоляции даёт фризы до 20 мс при каждом переключении. CUDA-OpenGL interop через cudaGraphicsResource снижает число переключений с 60 в секунду до 5, а фризы до 3 мс.

Виртуализация через vGPU на A100: каждый гость получает долю карты через vGPU-профиль, переключение между гостями идёт на уровне гипервизора и стоит 1-3 мс. Прямой passthrough через VFIO даёт нативную производительность, но карта целиком уходит гостю.

MPS на кластере с Slurm и Kubernetes настраивается на каждом узле: служба nvidia-cuda-mps-control стартует systemd-юнитом, pipe-каталог создаётся в /tmp/nvidia-mps с правами на группу пользователей GPU. Slurm-задачи подключаются к MPS автоматически, если переменная окружения установлена в prolog.

Для Kubernetes NVIDIA Device Plugin поддерживает MPS через параметр config в ConfigMap: resources с указанием nvidia.com/mig-1g.1g или mps долей. Поды с одинаковым конфигом делят MPS-прокси, и переключения контекстов исчезают внутри ноды.

Мониторинг MPS через nvidia-smi показывает прокси-процесс как владельца контекста, и счётчики отдельных клиентов доступны через MPS control daemon в режиме контроля:

echo get_server_list | nvidia-cuda-mps-control
echo get_client_list <pid> | nvidia-cuda-mps-control

Отказ MPS-прокси роняет все клиентские процессы, поэтому для продакшена настраивают рестарт через systemd с Restart=always, а приложения обрабатывают ошибку инициализации CUDA с повторной попыткой.

MIG делит GPU A100 или H100 на до семи инстансов с изолированными SM, L2-кешем и памятью. Настройка:

nvidia-smi -i 0 -mig 1
nvidia-smi mig -cgi 9,9,9,9 -C

Команда создаёт четыре инстанса профиля 9 по 20 ГБ каждый. Профили: 1g.5gb, 2g.10gb, 3g.20gb, 4g.40gb, 7g.80gb в зависимости от карты. Инстансы видны как отдельные устройства nvidia-smi -L, и каждый процесс видит только свой инстанс.

В контейнерах MIG-инстанс передаётся через NVIDIA_VISIBLE_DEVICES с указанием UUID инстанса. Kubernetes Device Plugin поддерживает MIG стратегию single и mixed с указанием профилей в resources.

Профилирование MIG-инстансов через nsys работает как на обычной карте, но события ограничены инстансом. Счётчики SM и памяти на инстанс доступны через DCGM, Data Center GPU Manager:

dcgmi dmon -i 0 -s pucm -d 1

DCGM собирает метрики для Prometheus через dcgm-exporter, и на графиках видно загрузку каждого инстанса отдельно, что позволяет балансировать нагрузку между инстансами.

Типичные сценарии оптимизации приложения

Inference-сервис с несколькими моделями на одной карте: MPS объединяет контексты моделей, и каждая модель получает долю SM через CUDA_MPS_ACTIVE_THREAD_PERCENTAGE. Батчирование запросов внутри модели и между моделями поднимает утилизацию SM с 40 до 80 процентов.

Обучение с распределёнными данными: один процесс на карту с NCCL для связи, переключений контекстов нет, MPS не нужен. Узкое место в связи, и решение в NVLink и правильной топологии.

Графический интерфейс плюс вычисления: разделение карт, CUDA-OpenGL interop для общих данных, или MIG с графическим инстансом на отдельной доле. На consumer-картах без MIG используется time-slicing драйвера с выставлением приоритетов через nvidia-smi -c EXCLUSIVE_PROCESS.

Пайплайн обработки данных: Producer-Consumer через streams внутри одного процесса вместо нескольких процессов на карте. Stream-механика внутри контекста эффективнее MPS: переключений нет вообще, только планирование ядер по зависимостям.

Итоговые рекомендации по GPU-среде

Правильная настройка GPU-среды начинается с вопроса: сколько процессов должно делить карту. Один процесс, переключений нет, streams внутри контекста. Несколько однотипных процессов, MPS с ограничениями через переменные окружения. Разные типы нагрузки с изоляцией, MIG с профилированием по ресурсам. Виртуализация с несколькими гостями, vGPU или passthrough по критичности латентности.

Порядок внедрения: 1) замерить текущее число переключений через nsys profile, 2) оценить долю потерь в p99 latency, 3) выбрать MPS или MIG по требованиям изоляции, 4) настроить ограничения ресурсов через переменные или профили, 5) замерить p99 и throughput после изменения, 6) настроить мониторинг через DCGM и nvidia-smi dmon с алертами на падение SM utilization.

Правильная настройка GPU-среды поднимает утилизацию SM с 40 до 80 процентов и снижает p99 latency на 30-50 процентов без замены железа. Один вечер настройки MPS или MIG окупается на inference-кластере за первую неделю.