
«Никелирование» 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%.


