Модель на гигабайт больше, чем ваша видеокарта. Движок предлагает компромисс: часть слоёв считать на видеокарте, часть — на процессоре. Звучит как разумная сделка, и половина владельцев карт на 8 и 12 гигабайт живёт именно так.
Прежде чем принимать эту сделку, полезно понять её цену. Она выше, чем кажется, и распределена не так, как подсказывает интуиция.
#Короткий ответ
Дороже всего обходятся первые же выгруженные слои, а не последние. Распространённое представление обратное — будто «немножко выгрузить почти бесплатно, а вот половину уже дорого». Арифметика говорит иначе.
Причина: слои считаются последовательно, и время на токен складывается из времени чтения весов с видеокарты и времени чтения весов из оперативной памяти. Оперативная память медленнее видеопамяти в разы. Как только на процессор уезжает хотя бы десятая часть модели, её вклад в общее время уже сопоставим с вкладом всей остальной модели.
Практический вывод: выгрузка — это не плавный регулятор, а обрыв. Если есть способ уместить модель целиком, он почти всегда лучше.
#Как это устроено
Чтобы выдать один токен, движок читает все веса модели. Слои на видеокарте читаются со скоростью видеопамяти, слои в оперативной — со скоростью системной памяти. Время складывается:
время на токен = вес_на_GPU / ПСП_видеопамяти + вес_на_CPU / ПСП_оперативной
Возьмём типичную связку: модель 8B в Q4_K_M весом 4,9 ГБ, видеокарта с пропускной способностью 360 ГБ/с, двухканальная DDR4-3200 с 51 ГБ/с. Разница между ними — семикратная. Считаем теоретические потолки при разной доле слоёв на процессоре:
| Слоёв на CPU | Время на токен | Потолок | Потеря от максимума |
|---|---|---|---|
| 0% | 13,6 мс | ~73 ток/с | — |
| 10% | 21,9 мс | ~46 ток/с | −37% |
| 20% | 30,0 мс | ~33 ток/с | −55% |
| 30% | 38,2 мс | ~26 ток/с | −64% |
| 50% | 54,6 мс | ~18 ток/с | −75% |
| 75% | 75,2 мс | ~13 ток/с | −82% |
| 100% | 96,1 мс | ~10 ток/с | −86% |
Что здесь смотреть: не абсолютные значения (у вас будут свои), а форму кривой. Между 0% и 10% теряется больше трети скорости. Между 50% и 100% — ещё около 44% от уже упавшего значения. Кривая крутая в начале и пологая в конце, а не наоборот.
Это расчёт, а не замер: реальные числа будут ниже за счёт накладных расходов на передачу данных по шине, но пропорции сохраняются. И именно пропорции здесь важны.
#Что из этого следует на практике
Первое правило: сначала попробуйте уместить всё. Порядок действий, прежде чем соглашаться на выгрузку:
- Сократить контекст — самый дешёвый по качеству ход.
- Включить квантизацию KV-кэша — почти бесплатно по качеству, вдвое дешевле по памяти.
- Взять квантизацию весов на ступень жёстче (Q5 → Q4_K_M).
- И только потом выгружать слои.
Модель 8B в Q4_K_M, целиком помещающаяся в видеокарту, почти всегда полезнее модели 14B, половина которой считается процессором. Полная лестница решений — в материале о том, что делать, если модель не влезает в память.
Второе правило: если выгружать, то минимально. Каждый оставленный на видеокарте слой ценен, поэтому подбирать нужно максимально возможное значение, а не «примерно половину».
#Процедура подбора числа слоёв
- Узнайте, сколько слоёв в модели. Число видно в логе запуска и в метаданных файла GGUF (поле с числом блоков). Для моделей класса 7–8B это обычно 28–32, для 14B — около 48.
- Запустите с максимумом:
-ngl 99. Если модель загрузилась — задача решена, дальше читать незачем. - При ошибке памяти уменьшайте бинарным поиском. Не по одному слою: начните с половины, затем делите оставшийся интервал. Пять-шесть запусков дают точное значение.
- Отступите на 2–3 слоя вниз от найденного максимума. Память под контекст расходуется по мере диалога, и значение, работающее на пустом контексте, упадёт на длинном.
- Замерьте на найденном значении и сравните с расчётным потолком из таблицы выше.
- Сравните с альтернативой: та же модель с более жёсткой квантизацией, но целиком на видеокарте. Часто второй вариант выигрывает и по скорости, и по удобству.
Флаг называется -ngl в llama.cpp и num_gpu в параметрах Ollama. Разбор остальных настроек — в материале о параметрах Ollama, которые действительно влияют на результат, и в гайде по llama.cpp без магии.
Третье правило: следите за тем, что выгружается автоматически. Инструменты подбирают число слоёв сами, и делают это по свободной памяти на момент запуска. Открытый браузер во время старта модели может стоить вам пяти слоёв на видеокарте.
#Где интуиция подводит
«Немного выгрузить — почти бесплатно». Как раз наоборот. Первые 10% выгруженных слоёв стоят больше трети скорости. Ощущение «бесплатности» возникает у тех, кто сравнивает не с полной загрузкой на видеокарту, а с полным расчётом на процессоре.
«Быстрая оперативная память решит проблему». Улучшит, но не решит. Даже DDR5 в двухканальном режиме отстаёт от видеопамяти дискретной карты в несколько раз. Механику разбирает материал о том, от чего зависит скорость LLM на процессоре.
«Шина PCIe — узкое место при выгрузке». Не она. Веса, лежащие в оперативной памяти, считаются процессором на месте, а не гонятся через шину на видеокарту каждый токен. По шине идёт только промежуточное состояние — небольшой объём. Узкое место — скорость самой оперативной памяти.
«Если модель влезла с частичной выгрузкой, значит всё в порядке». Она влезла на старте. Память под контекст растёт по мере диалога, и настройка, работавшая на первом сообщении, может отказать на двадцатом. Как считать расход заранее — в материале о том, сколько нужно VRAM для локальной LLM.
«Выгружать надо ровно столько, чтобы влезло». Плюс запас. Значение, найденное впритык, оставляет вас без резерва под рост контекста и под любую программу, которая займёт видеопамять.
#Частые вопросы
#Какое значение -ngl поставить?
Максимальное, при котором модель загружается, минус два-три слоя запаса. Значение 99 означает «все» и является правильной первой попыткой. Универсального числа нет: оно зависит от модели, контекста и того, что ещё занимает видеокарту.
#Сколько слоёв можно выгрузить без заметной потери?
Заметной будет уже выгрузка десятой части. Если формулировать порог по ощущениям: пока скорость остаётся выше 10–15 токенов в секунду, работать комфортно. Считайте по формуле, а не по доле слоёв.
#Почему модель загрузилась, но работает как на процессоре?
Скорее всего, на видеокарту не ушёл ни один слой: бэкенд не подхватился, и произошёл молчаливый откат. Проверяется командой ollama ps или строкой о числе выгруженных слоёв в логе llama.cpp.
#Лучше выгрузить слои или взять модель поменьше?
В большинстве случаев — взять модель поменьше или квантизацию пожёстче. Модель класса 8B целиком на видеокарте обычно полезнее модели 14B, наполовину считающейся процессором: она быстрее и не заставляет ждать.
#Влияет ли объём оперативной памяти на скорость выгруженной части?
Влияет её скорость, а не объём. Объём определяет, поместятся ли выгруженные слои вообще; скорость — с какой быстротой они читаются. Одна планка вместо двух означает вдвое меньшую пропускную способность и вдвое худший результат.
#Можно ли выгрузить KV-кэш отдельно от весов?
В некоторых движках есть такая возможность, но выигрыш от неё обычно отрицательный: кэш читается на каждом токене, и его вынос в медленную память бьёт по скорости сильнее, чем вынос части слоёв. Экономить память на кэше правильнее его квантизацией.