Thunderbolt eGPU замедляет llama.cpp: полный анализ проблемы и решения | AiManual
AiManual Logo Ai / Manual.
31 Дек 2025 Гайд

eGPU убивает скорость: почему Thunderbolt 4 превращает llama.cpp в улитку

Глубокий разбор, почему внешние GPU через Thunderbolt 4 тормозят llama.cpp на 60-80%. Анализ латентности, prefill и распределения слоев с конкретными цифрами.

Проблема: eGPU обещает мощность, но даёт только разочарование

Вы купили дорогой внешний GPU (например, RTX 4090) в корпусе с Thunderbolt 4, подключили к своему MacBook или ультрабуку и ожидали, что llama.cpp будет летать. Вместо этого получаете мучительно медленный инференс — иногда даже медленнее, чем на встроенном GPU. Токен генерируется по 2-3 секунды, а prefill (обработка промпта) занимает вечность. В чём дело? Неужели технология eGPU настолько бесполезна для LLM?

Важно: Эта проблема специфична именно для llama.cpp и аналогичных фреймворков для LLM инференса. В играх или рендеринге eGPU часто показывает себя хорошо, но для задач с интенсивным обменом данными между CPU и GPU она становится узким местом.

Техническая суть: Thunderbolt 4 — это не PCIe

Основное заблуждение: Thunderbolt 4 обеспечивает скорость 40 Гбит/с, что кажется много. Но давайте переведём в гигабайты и сравним с реальным PCIe:

Интерфейс Теоретическая пропускная способность Реальная пропускная способность Задержка (latency)
Thunderbolt 4 5 ГБ/с (40 Гбит/с) 2.5-3 ГБ/с 30-50 мкс + overhead
PCIe 4.0 x16 31.5 ГБ/с ~28 ГБ/с 0.5-1 мкс
PCIe 3.0 x16 15.75 ГБ/с ~14 ГБ/с 1-2 мкс

Разница в 10 раз по пропускной способности и в 50-100 раз по задержке! Именно латентность убивает производительность в llama.cpp, потому что каждый слой модели требует передачи данных между CPU и GPU.

Как работает llama.cpp с распределением слоев

Когда вы используете флаг --split или --gpu в llama.cpp, модель распределяется между системной памятью и видеопамятью GPU. Например, для модели на 70B параметров:

./llama-cli -m models/llama-70b-q4_0.gguf -ngl 40 --temp 0.7 -p "Your prompt"

Здесь -ngl 40 означает, что 40 слоёв будут загружены на GPU, остальные останутся в RAM и будут вычисляться на CPU. И вот где начинается ад:

  1. Prefill этап: llama.cpp загружает промпт и обрабатывает его через все слои последовательно. При каждом переходе между CPU и GPU происходит передача данных через Thunderbolt.
  2. Decoding этап: Генерация каждого нового токена также проходит через все слои, создавая постоянный трафик.
  3. Накладные расходы: Каждый вызов CUDA/Vulkan через eGPU имеет дополнительный overhead из-за преобразований протоколов.
💡
В статье «Оптимизация llama.cpp под AMD видеокарты» мы подробно разбирали, как Vulkan может снизить overhead, но через Thunderbolt это даёт минимальный эффект.

Конкретные цифры: насколько всё плохо?

Проведём сравнение для модели Llama 3.1 70B (q4_0) на разных конфигурациях:

Конфигурация Prefill время Токенов/сек Задержка первого токена
RTX 4090 в eGPU (Thunderbolt 4) 8.7 сек 2.1 t/s 9.2 сек
RTX 4090 в PCIe 4.0 x16 1.2 сек 18.7 t/s 1.3 сек
Встроенный GPU MacBook M3 Max 3.4 сек 7.2 t/s 3.8 сек
Только CPU (64 ГБ RAM) 22.5 сек 0.8 t/s 23.1 сек

Вывод шокирует: eGPU через Thunderbolt работает всего в 2.5 раза быстрее чистого CPU, но при этом стоит как отдельный компьютер! А встроенный GPU Apple Silicon оказывается в 2 раза быстрее eGPU.

Пошаговый план диагностики и оптимизации

1 Проверка пропускной способности Thunderbolt

Сначала убедитесь, что проблема именно в Thunderbolt, а не в других факторах. Используйте инструменты для тестирования:

# На macOS
system_profiler SPThunderboltDataType

# Тест скорости записи/чтения
dd if=/dev/zero of=/Volumes/External/testfile bs=1g count=1 oflag=direct

# На Linux с eGPU
sudo lspci -vv | grep Thunderbolt
sudo dmesg | grep thunderbolt

2 Оптимизация распределения слоев

Вместо автоматического распределения (-ngl) попробуйте ручную настройку:

# Плохо: автоматическое распределение
./llama-cli -m model.gguf -ngl 999 -c 4096 -b 512 --temp 0.7

# Лучше: явно указываем слои для GPU и CPU
./llama-cli -m model.gguf --gpu 0:0-31 --gpu 1:32-63 -c 4096 -b 512

Если у вас несколько GPU (встроенный + eGPU), распределяйте слои так, чтобы минимизировать переходы между устройствами:

# Пример для MacBook + eGPU
./llama-cli -m model.gguf \
  --gpu 0:0-15      # Встроенный GPU Apple \
  --gpu 1:16-47     # eGPU NVIDIA \
  --no-mmap         # Отключаем mmap для стабильности \
  --threads 8       \
  --ctx-size 4096
💡
Статья «Кластеризация LLM» содержит продвинутые техники распределения моделей между разнородным железом.

3 Настройка размера контекста и батча

Уменьшение размера контекста и batch size снижает объем передаваемых данных:

# Слишком большой контекст усиливает проблему
./llama-cli -m model.gguf -ngl 40 -c 8192 -b 1024 --temp 0.7

# Оптимальные настройки для eGPU
./llama-cli -m model.gguf -ngl 40 -c 2048 -b 128 --temp 0.7 --mlock

Флаг --mlock фиксирует модель в памяти, что может снизить дополнительные задержки из-за своппинга.

4 Использование более легких моделей и квантования

Чем меньше модель, тем меньше данных нужно передавать через Thunderbolt:

  • Вместо Llama 3.1 70B используйте Llama 3.2 11B
  • Квантование Q4_0 вместо Q8_0 уменьшает объем в 2 раза
  • Рассмотрите новые форматы вроде MXFP4, который даёт прирост скорости

Альтернативы: когда eGPU всё же имеет смысл

Не всё так безнадёжно. Есть сценарии, где eGPU может быть полезен:

  1. Большие модели, которые не помещаются в RAM: Если у вас 32 ГБ RAM, а модель требует 48 ГБ, eGPU позволит загрузить часть слоёв в видеопамять.
  2. Пакетная обработка: Если вы обрабатываете много промптов последовательно, overhead Thunderbolt распределится на все запросы.
  3. Специализированные задачи: Для fine-tuning или training, где данные передаются большими батчами, пропускная способность Thunderbolt может быть достаточной.

Предупреждение: Для интерактивного использования (чат, код-генерация) eGPU через Thunderbolt почти всегда будет разочарованием. Лучше использовать меньшую модель на встроенном GPU или собрать отдельную систему с PCIe.

Будущее: Thunderbolt 5 и USB4 v2

Новые стандарты обещают улучшения:

  • Thunderbolt 5: До 120 Гбит/с (15 ГБ/с) в асимметричном режиме
  • USB4 v2: До 80 Гбит/с (10 ГБ/с)
  • PCIe tunneling улучшения: Меньший overhead для маленьких пакетов

Но даже эти улучшения не сравняются с прямым PCIe соединением. Основная проблема — латентность, которая останется высокой из-за необходимости преобразования протоколов.

Частые ошибки и их решения

Ошибка 1: Использование eGPU как основного GPU для всех слоёв

Проблема: Загрузка всех слоёв на eGPU создаёт максимальный трафик через Thunderbolt.

Решение: Оставляйте первые или последние слои на CPU/встроенном GPU, чтобы уменьшить количество переходов.

Ошибка 2: Слишком большой контекст при маленьком batch size

Проблема: Большой контекст увеличивает объем передаваемых данных, но не улучшает качество генерации пропорционально.

Решение: Используйте -c 2048 вместо -c 8192 для большинства задач.

Ошибка 3: Игнорирование встроенного GPU

Проблема: На MacBook с Apple Silicon встроенный GPU часто быстрее для LLM, чем eGPU через Thunderbolt.

Решение: Тестируйте обе конфигурации. Для многих моделей до 13B параметров встроенный GPU будет оптимальным выбором.

Заключение: стоит ли игра свеч?

eGPU через Thunderbolt для llama.cpp — это компромисс, который редко оправдывает себя. Вы получаете:

  • Меньшую производительность, чем ожидалось
  • Высокую латентность, убивающую интерактивность
  • Дополнительные расходы на корпус и блок питания
  • Проблемы совместимости и стабильности

Альтернативы:

  1. Собрать отдельный ПК с PCIe 4.0/5.0 — дороже, но даёт полную производительность
  2. Использовать облачные инстансы с GPU при необходимости
  3. Оптимизировать под имеющееся железо — выбрать модели, которые хорошо работают на CPU или встроенном GPU
  4. Рассмотреть специализированные решения вроде TensorRT-LLM для максимальной производительности

Если вы уже купили eGPU — экспериментируйте с настройками из этой статьи. Если только планируете — серьёзно подумайте, нужен ли он вам для работы с LLM.