Современные графические процессоры используются не только для рендеринга графики, но и для общих вычислений (GPGPU): машинное обучение, научные вычисления, финансовое моделирование. В многозадачной среде несколько процессов могут одновременно требовать доступ к GPU. Без механизма вытеснения (preemption) длительно выполняющийся вычислительный kernel может заблокировать GPU на длительное время, что приводит к задержкам для других задач и снижению общей отзывчивости системы. Для серверов, обслуживающих интерактивные приложения и пакетные вычисления одновременно, вытеснение становится обязательным требованием к инфраструктуре: оператор облачной платформы не может допустить, чтобы один клиент монополизировал ускоритель на секунды или минуты.
Разберём механизмы вытеснения на GPU, их реализацию в Linux-драйверах, влияние на производительность и практические приёмы настройки.
Что такое вытеснение на GPU и типы вытеснения
Вытеснение (preemption) на GPU - это способность прервать выполнение текущего вычислительного ядра (kernel) и передать управление другому процессу или задаче с более высоким приоритетом. В отличие от CPU, где вытеснение происходит на уровне потоков ОС, на GPU вытеснение происходит на уровне командного потока (command stream) или выполнения шейдерного ядра.
Традиционно GPU выполнял команды последовательно в очереди команд (command queue). Если запущен длительный kernel, все последующие команды в очереди ждали его завершения. Такой подход делал невозможным смешивание интерактивной графики и тяжёлых вычислений без задержек. Современные GPU (NVIDIA Pascal/Volta/Ampere/Hopper, AMD RDNA/RDNA2/RDNA3, Intel Xe) поддерживают аппаратную вытеснение на уровне инструкций или на уровне задач (task-level preemption), что позволяет планировщику драйвера быстро переключать контексты между процессами.
Существует четыре основных типа вытеснения на GPU:
Уровень командной очереди (Command Queue Level)
Самый грубый уровень: GPU завершает текущий пакет команд (command buffer) перед переключением контекста. Задержка вытеснения равна времени выполнения всего командного буфера. Используется в старых архитектурах (NVIDIA Kepler, AMD GCN 1.0) и подходит только для пакетной обработки, где задержки не критичны.
Уровень задачи (Task-level preemption)
GPU может прервать выполнение на границе задач (task boundary). В CUDA это соответствует завершению kernel launch. В OpenCL - на границе enqueue-операций. Задержка вытеснения равна времени выполнения текущего kernel launch, что для длительных ядер машинного обучения может составлять десятки миллисекунд.
Уровень инструкций (Instruction-level preemption)
Самый тонкий уровень: GPU может прервать выполнение шейдерного ядра на уровне инструкций, сохранить состояние регистров и конвейера, переключить контекст и продолжить позже. Поддерживается в NVIDIA Pascal+ (Pascal, Volta, Turing, Ampere, Hopper) и AMD RDNA/RDNA2/RDNA3. Начиная с архитектуры Volta, NVIDIA также реализовала независимое планирование потоков внутри варпа (Independent Thread Scheduling), что упростило корректное возобновление вытесненных вычислений.
Графическая вытеснение (Graphics Preemption)
Отдельный режим для графического конвейера: прерывание на границе примитивов (primitive boundary), пикселей или вершин. Критично для VR/AR приложений, где задержка рендеринга напрямую влияет на пользовательский опыт и может вызывать укачивание при кадровой частоте ниже 90 FPS.
Реализация в Linux основные подходы
NVIDIA (Proprietary Driver)
NVIDIA драйвер использует Unified Memory Management (UVM) и Channel/Runlist механизм:
- Channel/Runlist: Каждому процессу выделяется канал (channel) с очередью команд (runlist). GPU планировщик переключает активные каналы.
- Preemption Mode: Настраивается через nvidia-smi или модуль nvidia.ko:
CODE0
AMD (amdgpu driver)
AMDGPU драйвер использует Hardware Scheduler (HWS):
- Hardware Scheduler: Аппаратный планировщик в firmware GPU управляет очередями (queues) и приоритетами.
- Queue Priority: Очереди имеют приоритет (0-15). Высокоприоритетные очереди могут вытеснять низкоприоритетные.
- GFX10+ (RDNA): Поддержка instruction-level preemption.
Intel (Xe / i915 driver)
Intel Xe/i915 использует GuC (Graphics microController):
- GuC Scheduler: Микроконтроллер управляет очередями выполнения и приоритетами.
- Preemption: Поддержка thread-level и instruction-level preemption на Gen12+ (Xe).
Во всех трёх драйверах нижним слоем выступает подсистема DRM (Direct Rendering Manager) ядра Linux с планировщиком drm/scheduler. Он распределяет задачи (jobs) по аппаратным очередям, учитывает зависимости между задачами через fence-объекты и инициирует восстановление при зависании: если задача не завершилась за отведённый таймаут, планировщик запускает полный сброс GPU (GPU recovery), прерывая все активные очереди и переотправляя незавершённые задачи.
Настройка вытеснения и приоритетов в драйверах
NVIDIA (nvidia-smi)
# Просмотр текущего режима вытеснения
nvidia-smi -q -d PREEMPTION
# Установка режима (требует перезагрузки или перезагрузки модуля)
# 0=off, 1=command buffer, 2=thread, 3=instruction
nvidia-smi -pm 1
AMD (sysfs)
# Просмотр доступных режимов
cat /sys/class/drm/card0/device/preemption_mode
# Установка режима
echo 2 > /sys/class/drm/card0/device/preemption_mode
Intel (i915)
# Включение GuC
echo 1 > /sys/class/drm/card0/gt/uc_fw_upload
# Настройка таймаутов вытеснения
echo 1 > /sys/class/drm/card0/gt/preempt_timeout_ms
Приоритеты задаются отдельно от режима вытеснения. Для NVIDIA используется CUDA MPS (Multi-Process Service) с разграничением активных клиентов:
export CUDA_MPS_PIPE_DIRECTORY=/tmp/nvidia-mps
export CUDA_MPS_LOG_DIRECTORY=/var/log/nvidia-mps
nvidia-cuda-mps-control -d
nvidia-smi -pm 1
nvidia-smi -pl 250 # power limit
Для AMD приоритет очереди (0-15) и количество очередей на приоритет задаются через sysfs:
echo 10 > /sys/class/drm/card0/device/sched_policy
echo 4 > /sys/class/drm/card0/device/sched_queues
Влияние на производительность детальный анализ
Издержки вытеснения
- Context Switch Overhead: Сохранение/восстановление контекста GPU (регистры, pipeline state, TLB). На NVIDIA Volta - ~5-10 микросекунд, на AMD RDNA2 - ~3-8 мкс.
- Pipeline Flush: Сброс конвейера инструкций при переключении контекста.
- TLB Shootdown: Инвалидация TLB при смене контекста адресного пространства.
- Cache Pollution: Вытеснение приводит к вытеснению данных из кэшей GPU, что снижает производительность возобновлённых kernel'ов на 2-5 процентов.
Выгоды
- Responsiveness: Интерактивные приложения (GUI, VR) получают доступ к GPU без долгих ожиданий.
- QoS: Гарантированное время доступа к GPU для задач с высоким приоритетом (VR, real-time rendering).
- Multitenancy: Несколько контейнеров/VM могут безопасно разделять один GPU без взаимных блокировок.
- Отказоустойчивость: зависший kernel можно прервать и сбросить без перезагрузки всей системы, что раньше требовало полного сброса GPU.
Издержки проявляются нелинейно: при частоте вытеснений до 100 в секунду накладные расходы обычно не превышают 1 процент времени GPU, тогда как при тысячах вытеснений в секунду накладные расходы могут поглотить до 15-20 процентов производительности. Поэтому режим вытеснения подбирается под профиль нагрузки, а не устанавливается по максимуму из соображений перестраховки.
Практический порядок настройки, примеры и рекомендации
- Определите требования: какие задачи требуют низкой задержки (VR, real-time), какие - пакетные (training, batch inference).
- Выберите режим вытеснения: Real-time/VR - instruction-level preemption, Compute/ML training - thread-level или task-level, Batch processing - command buffer level (минимальные накладные расходы).
- Настройте приоритеты: Real-time/VR - высокий приоритет (15 для AMD, high для NVIDIA MPS), Compute/ML - средний приоритет, Batch - низкий приоритет.
- Настройте таймауты: Preemption timeout - 1-5 мс для real-time, 10-50 мс для compute, Watchdog timeout - отключить или увеличить для длительных kernel'ов.
- Протестируйте под нагрузкой: запустите бенчмарки (MLPerf, SPECviewperf) и измерьте задержки.
- Настройте мониторинг: отслеживайте preemption count, context switch latency, GPU utilization.
NVIDIA (Data Center / ML Training)
# Включить Persistence Mode
nvidia-smi -pm 1
# Установить Power Limit для стабильных тактовых
nvidia-smi -pl 300
# Настроить MPS для мультитенантности
nvidia-cuda-mps-control -d
AMD (RDNA2/3 / ROCm)
# Включить HWS
echo 1 > /sys/class/drm/card0/device/hws_enable
# Настроить приоритеты
echo 1 > /sys/class/drm/card0/device/sched_policy
echo 4 > /sys/class/drm/card0/device/sched_queues
Intel (Xe / i915)
# Включить GuC
modprobe i915 enable_guc=2
# Настроить preemption
echo 1 > /sys/class/drm/card0/gt/uc_fw_upload
Типичные сценарии применения: рабочая станция с одновременным рендерингом в Blender и интерактивной работой в CAD требует instruction-level вытеснения, иначе интерфейс будет подвисать на время рендеринга. Облачный сервер инференса с множеством клиентов выигрывает от MPS и instruction-level вытеснения для справедливого разделения ускорителя. Выделенный узел обучения моделей может работать в режиме command buffer level, минимизируя накладные расходы, поскольку единственный потребитель GPU - пакетный процесс обучения.
Мониторинг, типичные ошибки и рекомендации по оптимизации
Метрики для отслеживания
- Preemption Count: Количество вытеснений в секунду (через perf или nvidia-smi/amdgpu sysfs).
- Context Switch Latency: Время переключения контекста (perf trace, nvidia-smi dmon).
- GPU Utilization: Утилизация GPU по времени (nvidia-smi dmon, amdgpu sysfs).
- Preemption Latency: Задержка от запроса вытеснения до фактического переключения.
Инструменты
- NVIDIA: nvidia-smi dmon, nvidia-smi pmon, perf, nvprof/nsight
- AMD: rocm-smi, amdgpu sysfs, rocprof, perf
- Intel: intel_gpu_top, intel_gpu_time, perf
Типичные ошибки при настройке: слишком агрессивная вытеснение для batch-нагрузок, игнорирование watchdog timeout для длительных kernel'ов и неправильные приоритеты, когда real-time задачи вытесняются пакетной обработкой. Отдельная распространённая проблема - миграция workload'ов между узлами с разными режимами вытеснения: приложение, отлаженное на рабочей станции с instruction-level вытеснением, может демонстрировать неожиданные задержки на сервере с режимом command buffer level, поэтому конфигурацию следует фиксировать в системной документации и проверять при развертывании.
Рекомендации по оптимизации: выбирайте режим вытеснения под нагрузку, настраивайте приоритеты под SLA приложений, следите за частотой вытеснений (более 1000 в секунду указывает на слишком агрессивный режим) и регулярно обновляйте драйверы и firmware, поскольку планировщики GPU активно дорабатываются вендорами. При диагностике подвисаний полезно включить детализированное логирование drm (/sys/module/drm/parameters/debug), которое показывает моменты вытеснения, таймаутов и восстановлений. Для длительного наблюдения собирайте метрики в систему мониторинга (Prometheus с экспортерами nvidia-gpu-exporter или amdgpu-exporter) и стройте графики распределения задержек, а не только средних значений: хвостовые задержки (p99) при вытеснении часто оказываются в десять-сто раз выше средних и именно они определяют пользовательский опыт интерактивных приложений.
Заключение по вытеснению вычислений на GPU
Вытеснение вычислений на GPU - критически важная технология для современных мультитенантных и real-time систем. Правильная настройка режима вытеснения, приоритетов и таймаутов позволяет достичь баланса между пропускной способностью (throughput) и отзывчивостью (latency). Для production-среды рекомендуется тестировать конфигурацию под реальной нагрузкой, поскольку синтетические бенчмарки не всегда отражают поведение системы при смешанных нагрузках интерактивных и пакетных задач.