Введение в оптимизацию производительности CPU и GPU
В высокопроизводительных вычислениях и обработке крупномасштабных параллельных задач GPU стали незаменимыми ускорителями. Чтобы полностью использовать вычислительные возможности GPU, крайне важно оптимизировать взаимосвязь CPU и GPU за счёт разумного распределения и привязки ядер CPU к GPU. Эта статья подробно рассмотрит концепции сокетов и NUMA (Non-Uniform Memory Access — неоднородный доступ к памяти), а также обсудит, как реализовать привязку ядер CPU и GPU на основе этой аппаратной архитектуры для обеспечения оптимальной производительности системы.
Понятие сокета
Что такое сокет?
Сокет обычно обозначает физический разъём для установки CPU на материнской плате. Каждый сокет соответствует одному физическому CPU, который обычно содержит несколько ядер и один или несколько уровней кеша (например, L1, L2, L3 кеш). В многопроцессорных системах (например, двух- или четырёхпроцессорных серверах) каждый сокет содержит физический CPU, которые соединяются через высокоскоростные интерконнекты (например, Intel QPI или AMD Infinity Fabric).
Характеристики многопроцессорных систем
В многопроцессорных системах CPU каждого сокета может обращаться к своей локальной памяти, а также к памяти других сокетов. Такая модель доступа к памяти вводит концепцию NUMA, направленную на оптимизацию эффективности доступа к памяти.
Архитектура NUMA (Non-Uniform Memory Access)
Что такое NUMA?
NUMA расшифровывается как Non-Uniform Memory Access, что означает неоднородный доступ к памяти. В отличие от традиционного однородного доступа (UMA), в архитектуре NUMA системная память разделяется на несколько регионов, каждый из которых связан с определённым CPU (сокетом). Доступ к локальной памяти (в пределах того же сокета) происходит быстрее, чем к удалённой памяти (из другого сокета), что приводит к более высокой задержке.
Узлы NUMA и задержка доступа к памяти
В системах NUMA каждый сокет и его непосредственно подключённая память образуют узел NUMA. Доступ к памяти в пределах одного узла NUMA происходит быстрее, тогда как межузловой доступ к памяти связан с дополнительной задержкой из-за передачи по шине. Оптимизация сродства (affinity) памяти и CPU, обеспечивающая выполнение задач в пределах одного узла NUMA, является критически важным шагом оптимизации производительности.
Физическая взаимосвязь CPU и GPU
Архитектура GPU
GPU обычно взаимодействуют с CPU через шину PCIe (Peripheral Component Interconnect Express). В многопроцессорных системах GPU обычно подключаются только к одному сокету (и соответствующему узлу NUMA), а не между сокетами. Это означает, что на практике GPU имеют более высокую пропускную способность и меньшую задержку при обмене данными с ядрами CPU и памятью того сокета, к которому они подключены.
Сродство CPU и GPU
Взаимодействие CPU и GPU в основном основано на передаче данных. Данные передаются от CPU к GPU и обратно к CPU, что включает операции доступа к памяти, существенно влияющие на производительность. Если ядро CPU, взаимодействующее с GPU, находится в том же узле NUMA, что и GPU, задержка передачи данных значительно снижается. Таким образом, привязка ядер CPU к GPU является ключевым шагом оптимизации производительности.
Стратегия привязки ядер GPU и CPU на основе Affinity
В развёртывании контейнеров Docker реализация привязки GPU и CPU на основе сродства (affinity) имеет решающее значение для повышения производительности задач в контейнерах. Функции управления ресурсами CPU и GPU в Docker позволяют точно контролировать, какие ядра CPU и GPU используют контейнеры.
Выделение ресурсов CPU и GPU контейнера
Контейнеры Docker позволяют точно управлять выделенными ресурсами CPU и GPU. Указывая, какие ядра CPU и GPU устройства использует контейнер, можно реализовать привязку на основе сродства, оптимизируя производительность.
Настройки CPU в Docker
В Docker распределение ресурсов CPU можно контролировать с помощью следующих параметров:
--cpuset-cpus: указывает физические ядра CPU, которые может использовать контейнер. Например,--cpuset-cpus="0-3"ограничивает контейнер использованием ядер с 0 по 3.--cpu-shares: управляет весом использования CPU контейнером, но не ограничивает использование конкретных ядер.--cpus: ограничивает общее количество ядер CPU (в виртуальных ядрах), которое может использовать контейнер.
Настройки GPU в Docker
Привязка GPU устройств может быть выполнена с помощью следующих параметров Docker:
--gpus: указывает GPU устройства, к которым контейнер имеет доступ. Например,--gpus '"device=0"'назначает GPU 0 контейнеру.
Реализация привязки ядер GPU и CPU в Docker
Чтобы реализовать привязку ядер GPU и CPU в контейнерах Docker, объедините настройки CPU и GPU. Ниже приведён пример привязки определённых ядер CPU и GPU при запуске контейнера Docker:
docker run --cpuset-cpus="0-3" --gpus '"device=0"' --memory="8g" my_gpu_container
В этой команде:
--cpuset-cpus="0-3"привязывает контейнер к ядрам CPU с 0 по 3, которые должны находиться в том же узле NUMA, что и GPU 0.--gpus '"device=0"'назначает GPU 0 контейнеру.--memory="8g"ограничивает использование памяти контейнером до 8 ГБ, что также обеспечивает согласованность выделения памяти со сродством CPU/GPU.
Достижение оптимальной привязки
Чтобы обеспечить оптимальную привязку в контейнерах, сначала определите физическую топологию машины. Используйте nvidia-smi topo -m для просмотра топологии:
nvidia-smi topo -m
Из вывода можно определить конфигурацию NUMA машины и распределение GPU. Например, если машина имеет два узла NUMA, NUMA 0 с 4 GPU и NUMA 1 с 4 GPU, вы можете идентифицировать сродство GPU и CPU для каждого узла NUMA.
Псевдокод для расчета Affinity
Ниже приведён упрощённый псевдокод для вычисления сродства и определения идентификаторов GPU и CPU:
type Affinity struct {
}
// Calculate whether the affinity is satisfied and return the corresponding GPU and CPU IDs
func calAffinity(affinity *Affinity, cpuUse []int, gpuUse []int, gpuReq int, cpuReq int) (bool, []int, []int) {
return true, []int{}, []int{}
}
Этот метод можно использовать для определения того, удовлетворяет ли узел требованиям оптимальной привязки. При наличии нескольких узлов этот метод можно применять для оценки и выбора наиболее подходящего узла.
Компания Novita AI разработала контейнерный движок, адаптированный для AI-вычислений нового поколения, который динамически корректирует алгоритмы для мониторинга использования оборудования в реальном времени и проводит сквозную оптимизацию. Пользователи могут наслаждаться максимальной вычислительной производительностью, не беспокоясь о технических деталях NUMA.
Пользователи также могут выполнять более продвинутые настройки NUMA в панели управления. Если у вас есть дополнительные требования, пожалуйста, свяжитесь с нами в Discord!
Посетите Novita AI для получения дополнительной информации!

