Инференс LLM на кластере старых ноутбуков. DIY или Сделай сам.. DIY или Сделай сам. llm.. DIY или Сделай сам. llm. Блог компании МТС.. DIY или Сделай сам. llm. Блог компании МТС. инференс нейросетей.. DIY или Сделай сам. llm. Блог компании МТС. инференс нейросетей. Машинное обучение.. DIY или Сделай сам. llm. Блог компании МТС. инференс нейросетей. Машинное обучение. Ноутбуки.
Инференс LLM на кластере старых ноутбуков - 1

Когда я взялся за этот небольшой эксперимент, мной двигало исключительно любопытство и желание ответить на вопрос — а что, если взять три моих старых ноутбука, объединить их вычислительные ресурсы и использовать их под инференс LLM? Идея запуска на них чего-то более-менее тяжелого выглядела заманчиво: три процессора, 52 ГБ оперативной памяти — в теории должно получиться быстрее, чем на любом из них по отдельности.

Спойлер: почти никогда не быстрее. Однако выяснение причины оказалось интереснее потенциальной истории успеха. Вылезли разные неочевидные вещи: старый процессор был не самым медленным, третий узел не особо помогал, а обработка промпта вела себя противоположно генерации токенов.

Сразу же оговорюсь — любой GPU средней ценовой категории сделает ту же самую работу в десятки раз быстрее. Но моя задача была увидеть узкие места в «естественной среде обитания». Приятного чтения.

Тестовый стенд

Делать упор на бренды ноутбуков не стану — это в целом не так важно. Укажу лишь общие характеристики:

Роль

CPU

Поколение

RAM

Тип

Coordinator

Core i7-4700MQ 

Haswell (2013) 

16 ГБ

DDR3

Worker

Core i5-8265U 

Whiskey Lake (2018) 

20 ГБ

DDR4

Worker

Core i3-1115G4 

Tiger Lake (2020) 

16 ГБ

DDR4

Итого — три поколения процессоров Intel с разбросом в семь лет. Тут разнородность не баг, а фича. Так можно увидеть эффект самого слабого узла в чистом виде. Отличие в наборах инструкций минимально — у всей троицы есть AVX2, FMA и F16C. Расширение AVX-512 присутствует только у Tiger Lake (i3), но в разнородном кластере это не станет преимуществом — llama.cpp работает по общему знаменателю.

Что до количества ядер — у i7 и i5 по 4 ядра / 8 потоков, а у i3 всего лишь 2 ядра / 4 потока. Можно подумать, что слабейший найден, но, как выяснится ниже, он вовсе не станет плестись в хвосте.

На всех узлах был развернут следующий софт:

  • Ubuntu 24.04 LTS 

  • llama.cpp, собранная из одного коммита 178a6c449 (build b10069). RPC-протоколу важно, чтобы на всех машинах версия была одинаковая.

  • governor выставил в performance (все ноутбуки от розетки)

Что же до сетевого подключения — любопытства ради присоединил все узлы к единому Wi-Fi 2.4 ГГц (увы, старенький i7 столько так умеет) и померял пинги:

rtt min/avg/max/mdev = 20.872/67.360/116.416/28.175 ms

Средний RTT ~67 мс, джиттер ~30 мс. Сразу же звучит как приговор: узлам надо обмениваться данными на каждый токен синхронно, а подобный разброс убьет всю производительность. Далее все узлы я подключил к обычной гигабитной сети:

rtt min/avg/max/mdev = 0.732/0.875/1.316/0.113 ms

Джиттер упал в 300 раз, средний RTT — в 85. Все следующие замеры стану делать, используя проводную сеть, а Wi-Fi оставлю как сюжет на будущее — надо будет выполнить апгрейд i7 до современных стандартов.

Методика измерений

В качестве инструмента взял llama-cli из llama-cpp. Каждую конфигурацию прогонял по три раза, бралась медиана. Для модели с 32b параметров снижал количество токенов, так как совсем медленно. Seed фиксированный, промпт одинаковый — всё, как доктор прописал.

Запуск распределенного инференса реализуется флагом –rpc со списком адресов воркеров. На последних при этом крутится ggml-rpc-server. Это, кстати, оказалось первыми граблями: я сначала по памяти безуспешно пытался запустить rpc-server. Выяснилось, что в свежих сборках его переименовали.

На воркерах:

~/llama.cpp/build/bin/ggml-rpc-server -H 0.0.0.0 -p 50052 -t "$(nproc)"

На координаторе:

~/llama.cpp/build/bin/llama-cli -m model.gguf 
--rpc 192.168.88.18:50052,192.168.88.77:50052 
-ngl 99 -n 128 -p "..."

В таком режиме локальная копия модели на воркерах не требуется — координатор сам загружает GGUF и раздает слои по сети. В качестве метрик выбрал обработку входного промпта (prompt eval) и генерацию ответа (token generation). Это важно, так как ведут они себя по-разному.

Результаты

Модель 1B (Llama-3.2-1B-Instruct, Q4_K_M)

Для начала взял небольшую модель, которая легко влезает в каждый узел, а инференс вполне комфортно работает на CPU. Для одиночных узлов получил следующие результаты:

Инференс LLM на кластере старых ноутбуков - 2
Таблица

Узел

Prompt eval t/s

Token generation t/s

i3-1115G4 

127.6

29.2 

i7-4700MQ 

103.9 

26.2 

i5-8265U 

108.5 

22.8 

Занятно, но двухъядерный i3 оказался быстрее всех. Тут, очевидно, играет роль не количество ядер, а тактовая частота и скорость ОЗУ. Tiger Lake способен держать до 4,1 ГГц (в boost-режиме), что значительно быстрее старого i7 (базовая 2,4 ГГц, буст 3,4 ГГц) и энергоэффективного i5 (базовая 1,6 ГГц, буст 3,9 ГГц), который в режиме полной нагрузки упирается в теплопакет. 

Перед тестом я полагал, что именно i5 будет самым шустрым, но гипотеза не подтвердилась. Как обычно — ожидания vs реальность. Но теперь самое интересное — распределенный инференс. Для сравнения туда же приложил данные лучшего solo теста (А):

Инференс LLM на кластере старых ноутбуков - 3
Таблица

Конфигурация

Узлы

Prompt eval t/s

Token generation t/s

A

i3

127.6

29.2 

B1

i3 + i5

70.3

22.5

B2

i3 + i5 + i7

67.0

22.6

B3

i5 через RPC

74.5

19.6

Хорошо видно, что на такой небольшой модели распределенка (22.5) подчистую проигрывает одиночному узлу (29.2) — отставание на 23%. Наиболее интересен B3 — это, по факту, тот же самый i5, который давал в solo 22.8, а обернутый в RPC теперь дает 19.6. Получается, что 14% скорости генерации теряется на то, чтобы поговорить с самим собой по TCP. Это значение — чистый налог RPC-протокола.

Модель 8B (Llama-3.1-8B-Instruct, Q4_K_M)

Теперь попробую модель у которой в восемь раз больше параметров и при этом влезает в каждый узел. В качестве референса запущу тесты по-отдельности в solo, а затем по RPC:

Инференс LLM на кластере старых ноутбуков - 4
Таблица

Конфигурация

Узлы

Prompt eval t/s

Token generation t/s

A1 (solo)

i3

1.5

4.9

A2 (solo)

i5

3.9

3.9

A3 (solo)

i7

2.9

4.4

B1

i3+i5

10.9

4.6

B2

i3+i5+i7

10.7

4.5

B3

i5 через RPC

11.3

3.9

Общая картина начала меняться. Теперь по генерации кластер (4.6) почти догнал лучший solo (4.9), а отставание сократилось с 23% до 6%. Опять результат B3 удивляет — RPC налог на генерации исчез. Объяснение, на мой взгляд, простое: модель стала достаточно тяжелой и поэтому сетевые накладные расходы перестали доминировать над вычислениями.

Еще одна любопытная деталь — обработка промпта. Если в одиночку i5 разгрызает его со скоростью 3.9 t/s, то кластер делает это почти втрое быстрее — 10.9 t/s. Причина в характере нагрузки: промпт может быть обработан батчем и параллельно (на разных узлах), а вот генерация — строго последовательный процесс (по токену за раз).

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

Модель 32B (Qwen2.5-32B-Instruct, Q4_K_M)

Выбор был сделан неслучайно — эта модель уже не влезет ни в один узел. Даже i5 с его 20 ГБ ОЗУ пасует — веса с KV-кешем и накладными расходами уведет ОС в своп (конфигурация A):

Инференс LLM на кластере старых ноутбуков - 5
Таблица

Конфигурация

Узлы

Prompt eval t/s

Token generation t/s

A (solo)

i5

0.7

0.3

B1

i3 + i5

2.4

1.1

B2

i3 + i5 + i7

1.9

1.1

Тот самый момент, когда распределенный инференс перестает быть «налогом» и становится единственно возможным способом запустить большую модель с «адекватной» скоростью. Разумеется 1.1 t/s — это медленно. Но все же в 3,7 раза быстрее, чем на i5, падающим в своп (бутылочным горлышком становится уже дисковый накопитель, а не ОЗУ).

Итоги dense-моделей

Если подвести общие результаты производительности:

Инференс LLM на кластере старых ноутбуков - 6
Таблица

Модель

Лучший в solo

Кластер (i3 + i5)

Победитель

1B

29.2

22.5

solo (+30%)

8B

4.9

4.6

паритет (solo + 6%)

32B

0.3 (своп)

1.1

кластер (+268%)

Хорошо видно, что перелом происходит между 8B и 32B — там, где модель перестает помещаться в один узел. До этого момента — сетевой оверхед есть, а ускорения нет.

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

MoE (gpt-oss-20b)

«А что там с MoE, позвольте поинтересоваться?» — спросите вы и будете абсолютно правы. Предыдущие примеры — это dense-архитектура, где при обработке каждого токена задействуются все параметры. 

Eсли взять модель класса Mixture-of-Experts (MoE), вроде gpt-oss-20b, то на каждый токен будет активироваться лишь примерно 3,6B параметров (32 эксперта, топ-4 на токен). Таким образом, в теории MoE должна работать на скорости маленькой модели, но со знаниями большой. Предлагаю взглянуть на реальность пока что на одном узле (i5) в solo:

Инференс LLM на кластере старых ноутбуков - 7
Таблица

Модель

Активных параметров

Token generation t/s

Llama-3.1-8B-Instruct

8B

~4–5

gpt-oss-20b

3,6B

~7–8

Первое наблюдение: в генерации MoE действительно быстрее dense-модели той же «весовой категории». Играет роль то, что на каждый токен приходится лишь 3,6B параметров. Эта магия прекрасно работает на CPU. Однако есть и ложка дегтя — MoE ускоряет вычисления, но не влияет на потребление памяти. Несмотря на то что на каждом шаге используется часть весов, все они должны лежать в RAM или VRAM.

На практике это означает, что gpt-oss-20b банально не влезла без свопа ни в один узел. Даже 20 Гб в i5 ей оказалось мало, а поэтому все данные solo-замеров получились «грязными». Взгляните на скорость обработки промпта:

prompt eval t/s по повторам на i5: 5.5 ; 17.2 ; 3.3

Один и тот же тест — от 3 до 17 токенов в секунду — характерный признак того, что модель то подгружается с диска, то берется из оперативной памяти. В кластере же все работает стабильно, без какого-либо разброса:

Инференс LLM на кластере старых ноутбуков - 8
Таблица

Конфигурация

Узлы

Prompt eval t/s

Token generation t/s

Разброс генерации

A (i5, solo)

i5

5.5

7.3

нестабильно

B1

i3 + i5

15.4

8.1

стабильно

B2

i3 + i5 + i7

15.9

8.0

стабильно

Ни один из узлов кластера не пользуется свопом — каждый берет данные из ОЗУ. Обработка промпта вновь выросла почти втрое (15.4 против 5.5) — сказывается параллельная работа узлов.

Подводя итог, можно сделать вывод, что на CPU заявленные преимущества MoE работают лишь наполовину. Да, скорость выше dense-модели, однако памяти требуется как для полной 20B. Выходит, что на таком железе получить адекватную производительность можно лишь в кластере, распределяющем веса по узлам.

Побочные наблюдения

Перед началом эксперимента мне казалось, что каждый новый узел будет добавлять полезную мощность, но в реальности все они становятся участниками синхронного обмена. Любое «слабое звено» станет просаживать показатели всего кластера. В моем случае старый Haswell с DDR3 не добавлял полезной мощности — дополнительная сетевая координация нивелировала его усилия. А в случае с 32B лишний узел увеличил обмен данными, но не скорость.

Обработка промпта и генерация токенов — совершенно разные истории. Распределенный инференс ускоряет первый процесс, но не влияет на второй. Получается, что если вам нужно обрабатывать длинные промпты с короткими ответами (например, суммаризация или RAG), то построение кластера действительно даст заметный прирост скорости. В случае же чат-бота с длинной генерацией — почти никак не повлияет.

В будущем хочу попробовать проделать те же тесты на Wi-Fi 7 (6 ГГц), а также на 2.5GBASE-T. У меня есть гипотеза, что с более быстрой сетью переломный момент сдвинется в сторону меньших моделей. 

Ну а если захотите повторить этот тест — буду рад, если поделитесь цифрами и наблюдениями в комментариях.

Автор: k0mar0v

Источник