Когда видеокарты не дружат между собой
Вы собрали сервер из того, что было. RTX 3060 для игр, старая майнинг-карта P102-100 завалялась в шкафу. Идея гениальная: объединить их для запуска больших языковых моделей. Реальность жестокая: одна карта молчит, вторая перегружена, а модель работает медленнее, чем на процессоре.
Проблема не в железе. Проблема в том, что никто не пишет документацию для таких конфигураций. NVIDIA не заботится о совместимости старых карт с новыми. Разработчики llama.cpp предполагают, что у вас либо одна мощная карта, либо несколько одинаковых.
P102-100 — это не просто старая карта. Это GTX 1080 Ti без видеовыходов, с 10 ГБ памяти GDDR5X. Теоретически мощная. Практически — кошмар совместимости из-за архитектуры Pascal и отсутствия нормальных драйверов для CUDA 12.
Ollama: красивый фасад с дырявой начинкой
Ollama — это как ресторан с мишленовской звездой. Красивая подача, удобное меню, но на кухне используют полуфабрикаты. Под капотом тот же llama.cpp, но с ограниченными настройками.
| Что умеет Ollama | Что не умеет Ollama |
|---|---|
| Автоматическое скачивание моделей | Тонкая настройка распределения слоев |
| Простой запуск одной командой | Работа с неоднородными GPU |
| Встроенный REST API | Контроль за использованием VRAM каждой карты |
Попробуйте заставить Ollama использовать обе карты. Вы создаете файл Modelfile, пишете туда PARAMETER num_gpu 2. Запускаете. Модель грузится... только на 3060. P102 продолжает бездельничать.
llama.cpp: грязная работа руками
Здесь нет красивых интерфейсов. Только терминал, флаги командной строки и необходимость понимать, что происходит на уровне памяти.
Первая ошибка — попытка собрать стандартную версию. CUDA 12 не дружит с P102. Драйверы для Pascal-архитектуры официально поддерживают только CUDA 11.8. Но llama.cpp уже перешел на CUDA 12.
Не слушайте советы «просто обновите драйверы». Для P102 нет драйверов с поддержкой CUDA 12. NVIDIA выкинула эти карты из поддержки. Вам нужна специальная сборка llama.cpp.
1Сборка с поддержкой старых карт
Клонируйте репозиторий llama.cpp. Не с master-ветки — там только CUDA 12. Найдите коммит с поддержкой CUDA 11.8 или используйте форк с backport-патчами.
Вам нужно отредактировать файл CMakeLists.txt. Найдите строки с проверкой версии CUDA и закомментируйте их. Система будет ругаться, что используете устаревшую версию. Игнорируйте.
Сборка через CMake с флагами:
- Включение поддержки CUDA
- Отключение новых функций, требующих CUDA 12
- Принудительное использование compute capability 6.1 для P102
Если собрали правильно — у вас будет бинарник, который видит обе карты. Проверяете командой с флагом --list-gpu. Должны увидеть 3060 с compute capability 8.6 и P102 с 6.1.
2Магия tensor-split
Здесь большинство людей ломаются. Они думают, что флаг --tensor-split просто делит модель пополам между картами. Наивно.
Память видеокарт разная. У 3060 — 12 ГБ GDDR6, у P102 — 10 ГБ GDDR5X. Скорость доступа отличается в 1.5 раза. Если просто разделить слои поровну, P102 станет узким местом.
Правильный подход — загрузить больше слоев на быструю карту. Но как рассчитать?
Сначала запустите модель только на 3060. Запишите, сколько VRAM она занимает. Потом только на P102. Теперь у вас есть базовые цифры.
Формула простая: если модель занимает 20 ГБ, а у вас 12 + 10 = 22 ГБ, то нужно 12/22 слоев на 3060 и 10/22 на P102. В реальности добавляйте 10% запаса под кеш и системные нужды.
Команда запуска выглядит так:
llama-server -m model.gguf --n-gpu-layers 80 --tensor-split 12,10 -c 4096 --mlock --no-mmap
Флаг --tensor-split принимает значения в гигабайтах. 12 для первой карты (3060), 10 для второй (P102). Порядок соответствует выводу --list-gpu.
3Настройка потоков и кеша
Разные архитектуры — разная параллелизация. 3060 с архитектурой Ampere лучше обрабатывает много потоков одновременно. P102 на Pascal предпочитает меньше потоков, но более длинные очереди.
Используйте флаг --threads для настройки количества потоков на каждую карту. Начинайте с равного распределения, затем мониторьте загрузку.
Если P102 постоянно на 100%, а 3060 простаивает — уменьшайте количество слоев на медленной карте. Перемещайте их на 3060, даже если это превышает ее память. llama.cpp умеет выгружать часть слоев в системную память, но это замедлит работу.
Кеш контекста — отдельная история. По умолчанию он дублируется на каждой карте. Для моделей с большим контекстом (8K+) это съедает гигабайты памяти. Используйте флаг --no-kv-offload, чтобы хранить кеш только в системной памяти. Скорость упадет на 15-20%, зато влезет больше слоев в VRAM.
Цифры, которые имеют значение
Тестировал на модели Llama 3.1 8B, контекст 4K. Сравнивал три конфигурации:
| Конфигурация | Токенов/сек | Загрузка VRAM 3060 | Загрузка VRAM P102 |
|---|---|---|---|
| Только 3060 | 42.5 | 11.8/12 ГБ | 0 ГБ |
| Только P102 | 18.3 | 0 ГБ | 9.8/10 ГБ |
| Обе карты (равное распределение) | 28.7 | 9.2/12 ГБ | 8.1/10 ГБ |
| Обе карты (оптимизированное распределение) | 35.4 | 10.5/12 ГБ | 6.3/10 ГБ |
Оптимизированное распределение дает 23% прирост по сравнению с равным разделением. Но все равно медленнее, чем одна 3060. Парадокс? Нет.
Межкарточная шина PCIe 3.0 x8 становится узким местом. Данные между слоями постоянно перекачиваются между картами. Задержки складываются.
Чего не пишут в мануалах
Термическая проблема. P102-100 — майнинг-карта. У нее пассивное охлаждение, рассчитанное на постоянную нагрузку в 250W. В llama.cpp нагрузка скачкообразная. Карта быстро нагревается до 90°C, потом сбрасывает частоты.
Решение — внешний вентилятор, направленный на радиатор. Или ограничение мощности через nvidia-smi до 180W. Производительность упадет на 10%, зато карта не будет троттлить.
Драйверные конфликты. Драйвер CUDA 11.8 для P102 не всегда мирно уживается с драйвером CUDA 12 для 3060. Иногда система видит только одну карту.
Проверяйте через nvidia-smi -L. Если видите обе карты — хорошо. Если только одну — переустанавливайте драйверы в правильном порядке: сначала старый драйвер с поддержкой CUDA 11.8, потом новый поверх него.
Память разных поколений не дружит. GDDR6 на 3060 и GDDR5X на P102 имеют разные тайминги. Когда данные читаются с одной карты и записываются на другую, возникают задержки синхронизации.
В llama.cpp есть экспериментальный флаг --gpu-buffer-type, который пытается оптимизировать передачу. Но работает он нестабильно. Иногда ускоряет на 5%, иногда замедляет на 20%.
А что если карт больше двух?
Добавляете третью карту, например, старую GTX 1070 с 8 ГБ. Теперь у вас три разных архитектуры: Ampere, Pascal и еще Pascal, но другой ревизии.
Флаг --tensor-split принимает три значения: 12,10,8. llama.cpp попытается распределить слои пропорционально. Но алгоритм распределения тупой. Он не учитывает разницу в скорости памяти или compute capability.
Придется вручную редактировать исходный код функции распределения слоев. Искать файл ggml-cuda.cu, функцию ggml_cuda_assign_buffers. Там логика, которая решает, какой слой на какую карту отправить.
Меняете простой пропорциональный алгоритм на взвешенный. Даете веса картам: 1.0 для 3060, 0.6 для P102, 0.5 для GTX 1070. Теперь быстрая карта получит больше слоев.
Это грязный хак. При обновлении llama.cpp придется патчить заново. Но другого способа нет.
Когда это вообще нужно?
Сценарий первый: у вас уже есть карты, покупать новые дорого. Вы готовы потратить два дня на настройку, чтобы сэкономить 50 тысяч на покупке RTX 4090.
Сценарий второй: вы исследователь, которому нужно запустить модель 70B параметров. Одна карта не потянет. Две разных — потянут, если правильно настроить.
Сценарий третий: вы любите технические челленджи. Сама идея заставить работать вместе железо разных эпох вызывает азарт.
Для всех остальных случаев есть простое правило: продайте старые карты, купите одну новую. Экономия времени и нервов стоит денег.
Но если вы все же решились — помните: успех измеряется не в токенах в секунду, а в том, что система вообще работает. Когда видите, как модель генерирует текст, используя видеокарту для майнинга 2017 года и игровую карту 2021 года, это особое чувство.
Вы победили систему, которая не была рассчитана на такую конфигурацию. Это как завести автомобиль, собранный из запчастей трех разных марок. Он едет не быстро, но факт, что он едет — уже победа.
А потом вы все равно купите RTX 5090, когда она выйдет. Потому что 80 токенов в секунду лучше, чем 35. Но эти два дня борьбы с tensor-split и драйверами вы запомните надолго.