Распределённая Gemma 31B на двух компьютерах
У меня дома две видеокарты по 16 гигабайт, но в разных компьютерах: RTX 4080 в десктопе и RTX 5060 Ti в домашнем сервере. Gemma 4 31B в нормальном 4-битном кванте весит 17 гигабайт и ни в одну из них не влезает. Можно взять iq3, но тогда страдает качество, и большой контекст всё равно не сделаешь.
А в llama.cpp есть RPC: на одной машине запускается rpc-server, который просто отдаёт свою видеокарту по сети, а на другой - обычный llama-server, который видит удалённую карту как ещё одно устройство и раскладывает по ним слои модели. Вот это я и попробовал.
Как оно устроено
- Десктоп с 4080 - координатор. На нём крутится
llama-server, модель можно взять прямо из папки LM Studio. - Сервер с 5060 Ti - воркер. На нём 24/7 крутится
rpc-server, без нагрузки он занимает 180 мегабайт видеопамяти. - Оба в docker, причём образ надо собирать самому: официальные образы llama.cpp собраны без RPC. И образ должен быть байт-в-байт одинаковый на обеих машинах, поэтому он собирается один раз и переносится через
docker save | docker load.
Модель - gemma-4-31B-it-qat-UD-Q4_K_XL от unsloth. Это QAT, то есть модель дообучали с учётом квантизации, и 4 бита у неё почти без потерь. Веса делятся примерно пополам, удалённая карта идёт первой, а локальная последней - на ней выходной слой, чтобы логиты не гонять по сети.
Что получилось
26 токенов в секунду на коротких запросах. Контекст 64K, и ещё влезает 4 параллельных запроса: каждый по 23 т/с, в сумме около 50.
Сеть почти не мешает. Упирается всё в память видеокарт: каждый токен - это прочитать все 17 гигабайт весов, а у 5060 Ti память в полтора раза медленнее, чем у 4080. Плюс карты работают по очереди, сначала одна свои слои считает, потом другая. Теоретический потолок по пропускной способности памяти около 32 т/с, так что 26 - вполне честно.
А дальше были сюрпризы.
MTP не работает. На одной видеокарте мне это давало ускорение в два раза. Тут скорость не растёт, на длинном контексте вообще падает вдвое: 7 т/с против 14. Каждый шаг с MTP стоит почти как три обычных токена, и чем длиннее контекст, тем хуже. Похоже, что-то в реализации гоняет по RPC лишнее. Я перебрал флаги, ничего не помогло, так что пока выключил.
KV-кэш в f16 быстрее, чем в q8. Я по привычке поставил q8_0: в два раза меньше памяти, а значит, думал я, и быстрее. А на 10K контекста f16 выдаёт 23.5 т/с против 20. Видимо, распаковать q8 дороже, чем прочитать вдвое больше. Правда, со 128K контекста f16 уже не влезает, поэтому я выбрал 64K и f16.
Ещё приятная мелочь: у llama-server есть --sleep-idle-seconds. При простое он выгружает модель с обеих карт и сам загружает её обратно на первый запрос. Первый ответ после сна приходит секунд через 45, зато видеопамять не занята, когда она не нужна.
Выводы
- Две видеокарты в разных компьютерах вполне складываются в одну через llama.cpp RPC, и 31B модель в 4 битах на них работает с нормальной скоростью.
- Параллельные запросы почти бесплатные: четыре одновременно идут почти с той же скоростью, что и один.
- MTP через RPC к сожалению ускорения не даёт.
- Квантованный KV-кэш экономит память, но может замедлять генерацию. Стоит замерить, а не ставить q8 по привычке.