GPU для локального ИИ достаточно дорогие, и мне стало интересно: а есть ли жизнеспособный инференс на CPU? А что будет если добавить дешевую видеокарту для майнинга или Tesla V100 16gb? Что можно выжать из максимально бюджетных решений для локального ИИ?

Я собрал бюджетную сборку в разных вариациях (2 материнские платы и 2 видеокарты) прогнал тесты некоторых известных моделей LLM и построил графики чтобы можно было сделать выводы.

Объем статьи достаточно большой, поэтому для простоты восприятия оформил 15-ти минутное видео на youtube.

Навигация по тексту:

Стенд

Для тестов я использовал 2 бюджетные платы на процессорах AMD.

MSI B450-A PRO (цена 4000р) + AMD Ryzen 7 5800X (8c/16t, 3.8GHz-4.7GHz) (цена 11000р):

  • Память работает в двухканальном режиме. Во втором и четвертом слоте память можно разгонять до 4000MHz, а в первом и третьем только 3200MHz, как итог: все 4 слота нормально работает только на 3200MHz.

  • PCIe 3.0 x16, PCIe 2.0 x8, M.2 на скорости PCIe 3.0 x4.

  • Цена за весь комплект: 16000р (вместе с кулером).

Asus ROG STRIX X399-E GAMING + AMD Ryzen Threadripper 2920X (12c/24t, 3.5GHz-4.3GHz):

  • Память работает в четырехканальном режиме на частоте 3400MHz.

  • 4 x PCIe 3.0 x16, PCIe 2.0 x4. 2 x M.2 на скорости PCIe 3.0 x4.

  • Цена за весь комплект: 26000р.

Разница в цене ~40% (16000р vs 26000р).

Сравнительная таблица:

Характеристика

MSI B450-A PRO + Ryzen 7 5800X

ASUS ROG STRIX X399-E GAMING + Threadripper 2920X

Материнская плата

MSI B450-A PRO

ASUS ROG STRIX X399-E GAMING

Процессор

AMD Ryzen 7 5800X (8c/16t, 3.8–4.7 ГГц)

AMD Ryzen Threadripper 2920X (12c/24t, 3.5–4.3 ГГц)

Память

Двухканальный режим. 3200 МГц.

Четырёхканальный режим, частота 3400 МГц

PCIe

PCIe 3.0 x16, PCIe 2.0 x8

2 × PCIe 3.0 x16, 2 × PCIe 3.0 x8, PCIe 2.0 x4

M.2

1 × M.2 PCIe 3.0 x4

2 × M.2 PCIe 3.0 x4

Цена комплекта

16 000 ₽

26 000 ₽

Далее по тексту первую сборку я буду называть AM4 (по названию сокета), а вторую Threadripper (по названию процессора).

В обоих случаях использовалась память DDR4 3600MHz от CUSU 4 плашки по 16гб (итого 64гб).

Кроме инференса только на CPU хочется узнать какой профит дает смешанный инференс CPU + GPU поэтому для тестов я использовал:

  • CMP 40HX 8gb (16 линий PCIe), цена ~6000р

  • Tesla V100 16gb, цена ~23000р

Модели ИИ

Если мы говорим про инференс на CPU, то нас интересуют MoE-модели, так как они используют только часть слоев при генерации ответа, что значительно ускоряет получение ответа (обсуждения вот и вот). Но если мы говорим про MoE, то возникает вопрос:

Какая деградация скорости при наполняемости контекста?

Это нам предстоит выяснить. А пока я готовил материал то нашел такое обсуждение.

Будем тестировать эти MoE-модели:

И посмотрим что могут плотные модели:

Большая часть тестов будет на MoE-моделях.

А как тестировать?

Decode

Для тестирования скорости генерации ответа я использовал промпт с длинной выдачей, порядка 7к-10к токенов:

Напиши полный код приложения рендера треугольника при помощи vulkan на c++ 17.

При помощи GPT-5.2 написал скрипт подсчета токенов и вывода статистики после каждой новой 1000 токенов. Сравнил с результатами из чата веб-интерфейса llama.cpp - сходится.

Сначала разогреваем кэши разогревочным запросом, а потом уже отправляем основной запрос и подсчитываем токены ответа, логируем информацию по каждой 1000 токенов:

  • gen_tok: сколько токенов сгенерировано

  • ctx: процент заполняемости контекста

  • win: какая тысяча токенов идет по счету

  • avg: средняя скорость генерации за это окно (за эту 1000 токенов)

  • curr: текущая скорость генерации

  • base: изначальная скорость генерации на первой тысяче токенов

  • degr: процент деградации скорости

  • cpu: текущее использование CPU

  • gpu: текущее использование GPU

  • gpu_wa: среднее использование GPU за это окно

Когда речь идет про GPU то возможны тесты как на одной так и на двух GPU, причем когда на двух то процент их использования суммируется.

Prefill

При помощи GPT-5.2 написал скрипт пошаговой генерации токенов (каждый шаг +1000 токенов) и вывода статистики на каждый шаг.

В этом тесте только синтетика, которая возможно слегка завышена, тем не менее это тоже показатель.

Здесь:

  • prompt_tok: количество токенов в промпте

  • ttft_ms: время до первого сгенерированного токена в милисекундах

  • prefill_tok_s: скорость обработки промпта, измеряется в токенах в секунду

  • degr_%: процент деградации по отношению к самой первой 1000 токенов, знак минус - деградация, знак плюс - увеличение скорости

  • cpu_wa%: среднее использование CPU на этом промпте

  • gpu0_wa_%: среднее использование GPU0 за это окно

  • gpu1_wa_%: среднее использование GPU1 за это окно

Пропускная способность памяти

Наверняка читатель уже не раз слышал что все упирается в пропускную способность памяти:

Пропускная способность (МБ/с) = Частота × Ширина шины × Количество каналов

Ширину шины можно узнать так (64 бит = 8 байт):

$ sudo dmidecode -t memory
...
Data Width: 64 bits
...

Посчитаем для сборки AM4: 3200 * 8 * 2 = 51 200 МБ/с

Посчитаем для сборки Threadripper: 3400 * 8 * 4 = 108 800 МБ/с

Однако, это теоретический максимум и он явно будет отличаться в меньшую сторону от реальных данных. А проверить реальную пропускную способность памяти можно при помощи STREAM:

# клонируем и переходим в директорию
$ git clone https://github.com/jeffhammond/STREAM
$ cd STREAM

# компилируем с увеличением размера данных, чтобы не напороться на кэши CPU
$ gcc -O3 -march=native -fopenmp -mcmodel=large \
    -DSTREAM_ARRAY_SIZE=200000000 \
    stream.c -o stream

# указываем количество потоков CPU для теста (12 из 24)
$ export OMP_NUM_THREADS=12

# запуск теста
$ ./stream

# сокращенный вывод программы STREAM для сборки на Threadripper
-------------------------------------------------------------
STREAM version $Revision: 5.10 $
-------------------------------------------------------------
Array size = 200000000 (elements), Offset = 0 (elements)
Memory per array = 1525.9 MiB (= 1.5 GiB).
Total memory required = 4577.6 MiB (= 4.5 GiB).
-------------------------------------------------------------
Number of Threads requested = 12
Number of Threads counted = 12
-------------------------------------------------------------
Function    Best Rate MB/s  Avg time     Min time     Max time
Copy:           74618.1     0.042986     0.042885     0.043178
Scale:          50370.7     0.063590     0.063529     0.063729
Add:            53119.1     0.091450     0.090363     0.099557
Triad:          53282.3     0.090208     0.090086     0.090344

В результатах тестов нас по большей части интересует строка Triad, так как она показывает чтение + запись + вычисления с данными: A[i] = B[i] + scalar * C[i].

А это 53282 МБ/с на Threadripper, что в 2 раза меньше теоретического максимума в 108 800 МБ/с.

А на AM4 в 16 потоков (к сожалению только этот результат сохранился) показывает 27038 МБ/с, что в 2 раза меньше теории в 51 200 МБ/с.

Можно протестировать скорость увеличивая потоки (Threadripper):

for t in 1 2 4 8 16; do export OMP_NUM_THREADS=$t; ./stream | grep Triad; done
Triad:          24207.7     0.201470     0.198284     0.203118
Triad:          36360.8     0.132164     0.132010     0.132546
Triad:          55285.7     0.086952     0.086822     0.087093
Triad:          54805.7     0.087722     0.087582     0.087955
Triad:          53087.3     0.091535     0.090417     0.097182

Как видно многопоточный тест на Threadripper показывает лучшую работу с памятью: 53087 МБ/с против 27038 МБ/с на AM4. Разница почти в 2 раза, значит и скорость будет отличаться в 2 раза? Не совсем, ниже по тексту выясним.

Разгон памяти и тайминги оставляю на откуп более опытным специалистам, я буду работать на auto.

Тесты инференса на CPU

Модели запускать я буду на llama.cpp. По большей части на сборке Threadripper, если не сказано иное. ОС: Debian 12. NVIDIA Driver: 580.

Когда мы говорим про MoE то сразу вспоминается эта статья про оптимизацию производительности. Однако, теперь это уже не работает и llama.cpp самостоятельно эффективно распределяет модель между CPU и GPU.

Тесты запускались с такими параметрами llama.cpp, если не указано иное:

--host 0.0.0.0
--port 8000
--ctx-size 32000
-np 1
--metrics
--jinja
--reasoning-format auto
-fa on
--no-mmap
-t 12
-ctk q8_0
-ctv q8_0

Docker или Host?

Изначально мне было лень разбираться в сборке llama.cpp из исходников на хосте, поэтому я использовал Docker-образ. Но так как речь идет про CPU где надо экономить на всем, то пришлось разобраться и собрать бинарник. Тесты я проводил на модели gpt-oss-120b, результаты почти одинаковые, большой разницы нет:

Однако, скорость загрузки отличается (gpt-oss-120b only CPU): на хосте 72 секунды, в docker 103 секунды.

Все последующие тесты я буду запускать на хосте.

Скомпилировать llama.cpp с поддержкой CUDA мне удалось только использую однопоточную компиляцию:

cmake --build build -j1

Разница в квантах

Сразу же встает вопрос о квантизации, чем крупнее квант тем лучше качество, но неизбежно меньше скорость. Сначала взглянем на графики, а потом краткие рекомендации по выбору квантов.

Gemma-4-26B-A4B-it на генерации ответа показывает ожидаемые результаты - чем крупнее квант тем медленнее, победа за mxfp4, а на обработке промпта mxfp4 хуже всех:

GLM-4.7 Flash на генерации показывает примерно такой же результат - лидирует mxfp4, на обработке промпта Q4_K_M выдает большую скорость, и меньшую UD-Q8_K_XL. Интересно, что чем больше контекста тем меньше разница между квантами при обработке промпта:

А у Qwen3.6-35B-A3B другая ситуация: при генерации самую высокую скорость показывает UD-Q4_K_XL, а при обработке промпта меньшие кванты показывают худшие результаты:

А теперь сравним все эти модели на кванте UD-Q4_K_XL. Здесь мы видим что хуже всех работает GLM-4.7 Flash, более того у этой модели деградация скорости от заполнения контекста выражена сильнее чем у других моделей.

Дополнительно почитать про кванты можно в обсуждениях llama.cpp на github (вот и вот) и на reddit (вот и вот).

Для себя я сделал такой вывод:

  • Q4_K_M и mxfp4 это нижняя граница, меньше не надо брать

  • UD‑Q4_K_XL будет точнее, а размер не сильно больше

  • Q6_K - лучший средний вариант

  • UD-Q8-K-XL - если позволяют ресурсы

Влияние частоты памяти

Первая составляющая пропускной способности памяти это частота. Давайте посмотрим на разницу скорости 2400MHz и 3400MHz на Threadripper:

Как видим при генерации на старте разница 16-13=3 токена, что составляет ~19%. А на 10к контекста разница всего-лишь 1 токен. При этом на обработке промпта разница в частоте памяти вообще никак не отражается.

Толком объяснений у меня нет, возможно знающие откликнутся в комментариях. Возможно, на старте мы имеем дело с малым KV-кэшом и с более предсказуемыми доступами к памяти, а чем дальше тем больше KV-кэш и энтропия доступа к памяти возрастает.

2 канала или 4 канала?

Другая составляющая пропускной способности памяти это канальность, чем выше канальность тем быстрее скорость работы. К этому тесту привлечем двухканальную плату на AM4 и четырехканальную Threadripper.

Напомню что Threadripper имеет в 2 раза больше пропускную способность памяти 53087 МБ/с против 27038 МБ/с на AM4. Разница почти в 2 раза. А разница в цене 40%.

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

А для особо дотошных вот результаты генерации ответа и обработки промпта:

Лучше всех себя показала gpt-oss-120b где самый низкий профит от повышения канальности составил ~12% на контексте 9к. А GLM-4.7 Flash показала ухудшение скорости от повышения канальности. Новые модели Qwen3.6-35B-A3B и Gemma-4-26B-A4B-it показали профит около 10%.

Тут я решил прибегнуть к высшему разуму к ИИ, но не задав вопрос “почему?”, а по другому:

Такое положение дел меня весьма удивило и я отправился вчитываться в ответ. Детали вы можете узнать сами, а если коротко то у Ryzen 7 5800X высокий IPC и частота CPU, а у Threadripper 2920X ниже IPC и ниже частоты CPU, NUMA 2 CCD (чтобы это не значило). Кажется, что цена Threadripper не стоит переплаты, по сравнению с получаемым результатом.

Число потоков

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

Если коротко:

  • prefill: много матричных операций, пригодно для распараллеливания

  • decode: каждый следующий токен генерируется последовательно, нельзя взять и сгенерировать 8 следующих токенов параллельно

Скорость dense моделей

Здесь все достаточно печально, и причина здесь в том, что в плотных моделях каждый токен в ответе должен пройти через все веса модели, в то время как у MoE моделей выходной токен проходит только через ограниченную часть:

Тесты инференса на CPU + GPU

С CPU понятно, медленно, но на MoE можно хотя бы початиться. А что будет если добавить видеокарты?

На модели gpt-oss-120b при добавление одной CMP 40HX 8gb происходит небольшой прирост как в decode так и в prefill. Tesla V100 16gb дает уже более ощутимый прирост, а вместе эти две карты дают максимальный прирост на генерации и чуть худший результат на обработке промпта.

Но тут сразу же стоит сделать оговорку: все зависит от распределения слоев, чем больше экспертных слоев для обработки вашего запроса легли на GPU тем больше будет скорость. Например, на графике ниже 4 разные загрузки модели на CPU + GPU дали 2 разных результата по скорости (то есть я загружал модель, тестировал, выгружал и так зафиксировал 4 итерации):

А вот результаты тестов еще и других крупных моделей Qwen3-Coder-Next и Qwen3-Next-80B-A3B-Thinking в сравнении с gpt-oss-120b:

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

Интересно что gpt-oss-120b показывает самый медленный результат при генерации, хотя и меньше всех использует GPU, но она также показывает низкую скорость на prefill, при этом больше всех используя GPU.

Тест: модель только на 2 GPU

Используя квант UD-4Q_K_XL и 32к контекста можно уместить в 24гб видеопамяти следующие MoE модели: Qwen3.6-35B-A3B, GLM-4.7 Flash, Gemma-4-26B-A4B-it. И 2 dense модели: Qwen3.6-27B, Gemma4-31B-it.

Как видно, dense модели имеют самые низкие показатели скорости, но эти показатели стабильны на всей длине тестового контекста.

Из MoE моделей только GLM-4.7 Flash показывает низкие результаты и значительную деградацию от размера контекста, а Qwen3.6-35B-A3B и Gemma-4-26B-A4B-it на prefill показывают рост скорости при росте контекста.

Если бы мы поместили модель целиком на одну видеокарту с 24гб видеопамяти, например на RTX 3090, мы бы получили значительно большие цифры, но мы рассматриваем бюджетные варианты. Если позволяют ресурсы, то можно взять 2 Tesla V100 16gb и результаты будут выше.

Какова цена?

Для начала определим минимум, который будет присутстовать в любой сборке:

Название

Цена

Примечание

ОЗУ 4 по 16гб

24000р

Цена часто скачет, но вполне реально купить по 12к за 2 плашки

Блок питания Azerty на 1050W

6000р

Один БП на 1200W оказался без защиты от КЗ, благо обошлось без последствий, лучше смотреть в сторону более качественных БП, пусть дороже, зато все железо в целости

Диск SSD на 512GB

7000

Цена взята с Ozon

Итого

37000р

Не считая мелочей (кабели, коннекторы и т.д.)

Материскние платы:

Плата

CPU

Цена

MSI B450-A PRO

AMD Ryzen 7 5800X

16000р

Asus ROG STRIX X399-E GAMING

AMD Ryzen Threadripper 2920X

26000р

Видеокарты:

Видеокарта

Объем памяти

Цена

CMP 40HX

8gb

6000р

Tesla V100

16gb

23000р

А теперь скомпонуем в 3 варианта:

Бюджет

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

Скорость

Вердикт

~53к

AM4 + 64GB RAM

Низкая

Для экспериментов

~63к

Threadripper + 64GB RAM

Низкая + 10%

Для экспериментов

~86к

Threadripper + 64GB RAM + Tesla V100 16GB

x ~1.8

Оптимальный бюджетный топ

Заметка про двухканальную сборку

Выше на графиках видно что канальность и частота памяти на Threadripper не дают пропорционального эффекта. Возможно, как подсказывает ИИ, здесь играют негативную роль низкий IPC и более низкие частоты работы CPU. Что делает более выгодным сборку на AM4.

Однако, в сборке на AM4 на максимальной частоте завелись только 2 слота ОЗУ, если бы удалось завести все 4 слота на максимальной частоте (от 3600MHz), то возможно это выдавало бы аналогичные скорости как и у Threadripper, а в некоторых моментах может быть и больше, как это вышло с моделью GLM-4.7 Flash.

Пока дописывал статью, обратил внимание на Intel Сore i5 10600kf и платы MSI Z490 Tomahawk из этого обсуждения, по цене примерно также как текущая сборка на AM4, однако есть предположение что c разгоном там дела получше чем на b450.

Можно быстрее?

Если говорить про видеокарты (про бюджетные решения), то конечно можно, убираем CMP 40HX 8gb и вставляем вторую Tesla V100 16gb - дороже, но больше видеопамяти, скорость будет быстрее.

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

Что касается Threadripper, возможно на TRX40 (по этому запросу можно искать на Авито) скорость будет выше, однако здесь цены сборок значительно выше выше.

А если говорить про 8-ми канальный режим памяти доступный только на WRX80 / Threadripper Pro (по этому запросу можно искать на Авито) (процессор должен быть именно Threadripper Pro) то цены могут быть еще больше, минимальная сборка, которую мне удалось отыскать составляет ~90к за материнскую плату + процессор, при этом его профитность (по соотношению цена / скорость) пока не понятна.

Кроме Threadripper стоит посмотреть в сторону восьмиканальных Epyc, цены за комплекты плата + процессор колеблятся в пределах 50-70к рублей на Авито.

Но частота процессора значительно ниже чем у Threadripper, как это скажется на инференсе не до конца понятно. К тому же здесь уже не десктопные DDR4, а серверные, что может стать проблемой.

Практическое применение

Эту сборку можно использовать в чатах OpenWebUI или в агентских инструментах, в том числе для программирования Claude Code (через CCR) / OpenCode и им подобные, а также можно использовать для генерации контента при помощи ComfyUI. Но есть нюансы:

  1. скорость - обработка промпта и генерация идут медленно, а на MoE моделях скорость генерации снижается с увеличением контекста. Для ускорения нужно как можно больше перемещать весов модели в видеопамять.

  2. контекст - чем больше вам нужно контекста тем больше он будет занимать памяти, а это значит что вам нужно больше видеопамяти для быстрой обработки и ответа либо больше времени и терпения если это будет в ОЗУ.

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

Завершение

Компоненты от Threadripper я пустил на развитие другой сборки на базе четырех RTX 3090, так как плата оснащена двумя слотами × PCIe 3.0 x16 и двумя × PCIe 3.0 x8 что позволяет полностью раскрыть вычислительную мощностью видеокарт в инференсе. Возможно, об этой сборке мы поговорим в другой статье.


В своем телеграм-канале (редкие посты, только по делу) я выкладываю посты про мои исследования локального инференса. На youtube-канале публикую видео по той же теме (также редко). У себя на сайте выкладываю инструкции.