AI и DIY,  Статьи

«Никелирование» llama.cpp на компьютере с несколькими GPU Nvidia

Программное окружение llama.cpp является одним из лучших инструментов для работы с локальными AI-моделями. Сообщество активно развивает проект: регулярно появляются новые оптимизации, поддержка аппаратных ускорений и рост скорости инференса.

Одной из «фишек» llama.cpp является поддержка практически любых видеокарт, а также возможность распределения больших AI-моделей на нескольких видекоартах. В этом случае большое влияние на скорость проведения вычислений оказывает полоса пропускания общей шины памяти, то, насколько быстро производится обмен данными между GPU, CPU и RAM компьютера.

Упрощенная формула для расчета скорости инференса в зависимости от пропускной способности памяти:

Реальная скорость проведения вычислений ниже теоретической, зависит от bandwidth памяти и скорости interconnect между картами. Из-за различных задержек система выдает около 60–90% от максимума.

Для увеличения быстродействия при обмене данными нужно:

  • увеличивать до максимума частоту VRAM видеокарт;
  • разгонять частоту работы шины данных материнской платы и ОЗУ;
  • отключать ненужные системные службы и пользовательские программы;
  • устанавливать видеокарты в самые быстрые разъемы PCI-E материнской платы (предпочтительно x16 Gen4/Gen5 или хотя бы Gen3 на старом железе);
  • отказаться от использования райзеров, особенно ограничивающих пропускную способность до PCI-Ex1 Gen1-2;
  • включать в BIOS матринской платы самые последние поддерживаемые стандарты работы по шине PCI-E (например, использовать стандарт Gen.3 вместо поколений Gen.1 или 2:

Как работает NCCL (NVIDIA Collective Communications Library)?

Одним из способов дополнительного увеличения скорости обмена и уменьшения латентности в системе с несколькими видеокартами является использование технологии Nvidia Collective Communication Library (NCCL, произносится «Nickel»).

NCCL позволяет оптимизировать работу с данными как внутри одного компьютера, так и по сети. Библиотека автоматически анализирует топологию (PCIe, NVLink, NVSwitch, InfiniBand и др.) и выбирает оптимальные алгоритмы (Ring, Tree, AllReduce и т. д.), что значительно увеличивает производительность при инференсе на нескольких GPU за счет обеспечения прямого взаимодействия между CUDA-устройствами, снижая задержку и улучшая совместное провдение вычислений и связь.

Преимущества NCCL:

  • прямой доступ GPU к памяти друг друга.
  • значительное снижение overhead при multi-GPU.
  • поддержка многопоточных и MPI-приложений.

NCCL, в основном, использует два алгоритма — Ring и Tree (включая вариации вроде Double Binary Tree / NVLSTree). Конкретный алгоритм выбирается в зависимости от:

  • размера сообщения (message size);
  • топологии системы (PCIe, NVLink, InfiniBand, количество узлов);
  • конкретной коллективной операции;
  • протокола (LL/LL128 для малых сообщений, Simple для больших).

При использовании алгоритма Ring GPU логически выстраиваются в кольцо (unidirectional ring): каждый GPU общается только с двумя соседями (prev и next).В этом случае, как правило, данные разбиваются на части, каждая GPU накапливает (reduces) свою часть, а затем они распространяются по кольцу, конвейерно (pipelined).

Ring GPU algo имеет следующие преимущества:

  • bandwidth-optimal (максимально используется пропускная способность);
  • все связи загружены одновременно;
  • идеально для больших сообщений (large tensors/градиентов);
  • латентность масштабируется линейно с числом GPU.

В связи с этим, с увеличением числа видеокарт на компьютере растет и латентность (увеличиваются задержки). При установке одной или нескольких видеокарт в медленный слот пострадает общее быстродействие, снизившись до величины «слабого звена». При обмене маленькими сообщениями алгоритм Ring действует с меньшей эффективностью, чем Tree.

Алгоритм Tree NCCL GPU организует обмен данными по иерархии, обычно используя для этого механизм double binary tree (два дополняющих бинарных дерева):

  • одно дерево для reduction (снизу вверх);
  • второе — для broadcast (сверху вниз);

Двойная структура системы связи обеспечивает баланс: ни один узел не является non-leaf в обоих деревьях одновременно. В ней «листья» отправляют данные родителям, промежуточные узлы агрегируют и передают дальше. Латентность такой системы является оптимальной, она лучше работает с маленькими сообщениями, Tree лучше работае на компьютерах с разными картами и слотами (гетерогенная топология), а также хорошо подходит для Broadcast/Reduce.

В то же время, Tree algo хуже работает при обмене большими сообщениям, не всегда имеет оптимальную полосу пропускания (может быть congestion у root/leaves). Обычно Tree NCCL включается в случае преобладания обмена маленькими/средними сообщениями, при multi-node сценариях, высокой латентности сети, ее большом масштабе.

На практике использование алгоритма Ring даёт близко к 100% bandwidth, но, в то же время, растёт latency. Алгоритм Tree может дать до 180x лучшую латентность на очень большом масштабе (множество GPU).

NCCL автоматически переключается между этими алгоритмами (есть NCCL_TREE_THRESHOLD), но, при желании, можно принудительно задать желаемый алгоритм через export NCCL_ALGO=Ring или Tree (или комбинацию).

Как установить поддержку NCCL в Linux?

Исходный код NCCL доступен на github, репозиторий NVIDIA/nccl, библиотека входит в состав NVIDIA HPC SDK и доступна в бинарном виде на сайте Nvidia в разделе для разработчиков.

При сборке llama.cpp без установленной библиотеки NCCL появляется предупреждение:

-- Could NOT find NCCL (missing: NCCL_LIBRARY NCCL_INCLUDE_DIR) 

-- Warning: NCCL not found, performance for multiple CUDA GPUs will be suboptimal

Бинарные файлы все равно создаются, но их быстродействие меньше, чем могло бы быть при работе в multi-GPU окружении.

Как установить библиотеку NCCL в linux?

В ubuntu NCCL можно установить командой:

  sudo apt update && sudo apt install libnccl2 libnccl-dev

В любой ОС на ядре linux можно собрать и установить NCCL из исходников, например, следующими командами (для Nvidia Tesla+Turing, CUDA 12.9 + принудительное использование gcc14) :

git clone https://github.com/NVIDIA/nccl.git /tmp/nccl

cd /tmp/nccl

make clean

CC=gcc-14 CXX=g++-14 \

make -j$(nproc) src.build \

CUDA_HOME=/opt/cuda \

NVCC=/opt/cuda/bin/nvcc \

NVCC_GENCODE="-gencode=arch=compute_70,code=sm_70 -gencode=arch=compute_75,code=sm_75"

sudo make install CUDA_HOME=/opt/cuda NVCC=/opt/cuda/bin/nvcc

После сборки и установки проверяем успешность соверешенных действий командой

  ls /usr/local/lib/libnccl* && ldconfig
/usr/local/lib/libnccl.so    /usr/local/lib/libnccl.so.2.30.7

/usr/local/lib/libnccl.so.2 /usr/local/lib/libnccl_static.a

После этого можно собрать llama.cpp для multi-GPU системы, для видеокарт Nvidia Tesla V100 + Nvidia GeForce GTX 1660-й серии используем следующие команды:


git clone https://github.com/ggml-org/llama.cpp && cd llama.cpp && mkdir build && cd build

cmake .. -DGGML_CUDA=ON -DCMAKE_CUDA_ARCHITECTURES="75;70"

cmake --build . --config Release -j$(nproc)

Теперь при сборке llama.cpp ошибка не возникает, создаются бинарники с поддержкой технологии nccl:

...
-- Using CMAKE_CUDA_ARCHITECTURES=70;75 CMAKE_CUDA_ARCHITECTURES_NATIVE=70-real;75-real
-- Found NCCL: /usr/local/lib/libnccl.so
-- CUDA host compiler is GNU 14.3.1
-- Including CUDA backend
...
-- Build files have been written to: /home/intel35/Рабочий стол/llama.cpp/build

Благодаря этому увеличивается скорость обмена между памятью нескольких GPU Nvidia, что увеличивает быстродействие при AI-инференсе. Прирост становится особенно заметен в конфигурациях с 3+ GPU, где задержки при обмене суммируются.

Для достижения максимального эффекта при использовании nccl нужно использовать параметр split-mode tensor (если видеокарты поддерживают тензорные вычисления аппаратно). Даже без него прирост в скорости обмена на компьютере с несколькими GPU без тензорных ядер будет заметен.

Пример скрипта для запуска llama.cpp на компьютере с несколькими видеокартами с тензорными ядрами:

  ./llama-cli -m model.gguf -ngl 99 --split-mode tensor --tensor-split 0.5,0.5 -p "Hello"

При использовании llama.cpp, собранного без поддержки NCCL в nvidia-smi заметны простои видеокарт из-за задержек по каналу связи, с включенной библиотекой NCCL нагрузка видеокарт становится ближе к 100%.

На компьютере автора с тремя видеокартами Nvidia был достигнут прирост скорости в llama.cpp до 10%:

Решение проблем при использовании технологии NCCL

Для отладки и диагностики работы приложений с nccl можно использовать следующие переменные:

  export NCCL_DEBUG=INFO # посмотреть, какой алгоритм выбран
  export NCCL_DEBUG_SUBSYS=ALL
  export NCCL_DEBUG=WARN          # или INFO для диагностики
  export NCCL_IB_DISABLE=1        # для  PCIe-систем
  export NCCL_SOCKET_IFNAME=^lo,docker
  export NCCL_ALGO=Ring # принудительно работать с алгоритмом Ring (или Tree, CollNet, NVLS)
  export NCCL_TREE_THRESHOLD=... # порог размера сообщения для Tree

При возникновении ошибки NCCL not found проверяем корректность установки, переустанавливаем libnccl-dev.

В логах NCCL (NCCL_DEBUG=INFO) можно просмотреть какой из алгоримтов используется, узнать информацию о канале связи, протоколах (LL/LL128/Simple) и топологию.

При проблемахсо сборкой проверяем соответствие используемого компилятора GCC и версии Nvidia Cuda Runtime на компьютере.

При проблемах со связью между картами, при запуске скрипта используем переменные NCCL_DEBUG=INFO и разные NCCL_ALGO.

Если возникают ошибки при запуске или достигается малый прирост скорости, можно попробовать использовать параметр split-mode layer вместо split-mode tensor.

Заключение

Включение NCCL — относительно простая оптимизация, которая даёт ощутимый прирост скорости локального AI-инференса в multi-GPU окружениях (особенно на 3+ картах). На системе автора данной статьи с тремя видеокартами NVIDIA прирост составил около 10%.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *