Распределённая Gemma 31B на двух компьютерах

У меня дома две видеокарты по 16 гигабайт, но в разных компьютерах: RTX 4080 в десктопе и RTX 5060 Ti в домашнем сервере. Gemma 4 31B в нормальном 4-битном кванте весит 17 гигабайт и ни в одну из них не влезает. Можно взять iq3, но тогда страдает качество, и большой контекст всё равно не сделаешь.

А в llama.cpp есть RPC: на одной машине запускается rpc-server, который просто отдаёт свою видеокарту по сети, а на другой - обычный llama-server, который видит удалённую карту как ещё одно устройство и раскладывает по ним слои модели. Вот это я и попробовал.

Как оно устроено

Модель - 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, зато видеопамять не занята, когда она не нужна.

Выводы