Gemma 4 26B на 13-летнем Xeon: 5 токенов в секунду без GPU
Меня периодически спрашивают: «А можно запустить какой-нибудь LLM локально, без облака?» Обычно я отвечаю что-то про требования к видеокарте и дороговизну нормального железа. И вот недавно наткнулся на историю, которая заставила меня пересмотреть эту позицию.
Человек взял сервер тринадцатилетней давности — два процессора Xeon, обычная DDR3 память, без единой видеокарты — и запустил на нём Gemma 4 26B. Не какой-нибудь урезанный 7B, а полноценную 26-миллиардную модель с mixture-of-experts архитектурой. Скорость — примерно пять токенов в секунду. Это скорость чтения человеком.
Звучит как какой-то хак из далёкого будущего, но это произошло прямо сейчас, на железе, которое многие уже списали на помойку.
Железо, на котором это запустили
Итак, что за сервер:
Сервер представлял собой переделанный HP StoreVirtual — хранилище данных, которое сняли с производства. Два процессора Intel Xeon E5-2690 v2 (Ivy Bridge, 2013 год). Никаких дискретных видеокарт. Память — DDR3. Общая стоимость всей системы — меньше трёхсот долларов, включая железо и его подготовку.
Для справки: Ivy Bridge — это архитектура Intel, которая вышла в 2012 году. Она поддерживает только набор инструкций AVX1. Современные оптимизированные библиотеки для запуска языковых моделей обычно рассчитаны на AVX2 или даже AVX-512, которые появились на поколение позже.
АРХИТЕКТУРНАЯ ЛИНЕЙКА INTEL XEON
─────────────────────────────────
Ivy Bridge ──► Haswell ──► Broadwell ──► Skylake
2012 2013 2014 2015
│ │ │ │
AVX1 AVX2 AVX2 AVX2
(только) + FMA3 + AVX512 + AVX512
◄── Сервер из статьи ◄── Современный стандарт
Модель и софт
Модель — Gemma 4 26B-A4B. Это вариант Gemma с mixture-of-experts (смешанные эксперты, MoE) архитектурой от Google. В двух словах: вместо того чтобы активировать все 26 миллиардов параметров для каждого токена, модель использует только часть из них (8 экспертов из 16). Отсюда и название — 26B-A4B, где A4B означает «approximately 4 billion active parameters».
Для запуска использовался ik_llama.cpp — форк библиотеки llama.cpp от ikawrakow. Этот форк добавляет оптимизации, критичные для Gemma с её mixture-of-experts: speculative decoding, маршрутизацию экспертов с учётом особенностей CPU, порт flash attention для процессора, перепаковку весов в рантайме.
Где всё сломалось
Первая попытка запустить модель провалилась. Программа падала при старте. Библиотека была собрана с оптимизациями под AVX2, а процессор понимал только AVX1.
Но настоящая проблема оказалась глубже. После отключения быстрых AVX2-маршрутов через флаг GGML_USE_IQK_MULMAT автор обнаружил, что Gemma генерирует бессмыслицу. Текст выглядел как беглый многоязычный поток сознания: тайский, корейский, обрывки английского — и всё это вперемешку в одном предложении.
Детерминированная бессмыслица. При нулевой температуре генерация повторялась побитово. Никаких NaN в вычислениях. В этом и крылась подсказка.
Автор обратился к Claude (модель рассуждений) с просьбой разобраться. Claude замерил логиты (числа, которые модель использует для выбора следующего токена) перед сэмплированием и увидел картину: среднее значение логита было около +16, когда должно быть близко к нулю. Около восьмидесяти процентов токенов из словаря в 262 000 единиц имели положительные логиты. Такое происходит, когда значительная часть скрытого состояния — неинициализированная память с мусором.
Корневая причина: в библиотеке два оператора — MOE_FUSED_UP_GATE и FUSED_UP_GATE — использовались в графе вычислений Gemma, но не были обработаны в диспетчере при отключённом GGML_USE_IQK_MULMAT. Они просто выпадали в default case и ничего не делали. Gemma со своими 30 слоями и 8 активными экспертами на каждом токене тихо складывала 240 тензоров с мусором из памяти.
Как починили
Исправление заняло три коммита.
Компиляционные правки. Скалярные ветки в квантизации не были по-настоящему скалярными — всё ещё вызывали AVX2-хелперы. Переписали на портативные циклы, добавили недостающие guards и include-файлы.
Рантайм-баг. Вместо изменения диспетчера решили сделать так, чтобы граф вычислений генерировал операторы, под которые уже есть рабочие реализации. Для MOE-ветки: разбили объединённый тензор up_gate_exps на два отдельных, выполнили два ggml_mul_mat_id вызова и объединили результат через ggml_fused_mul_unary с функцией активации SiLU.
CI-заглушки. Несколько тестовых функций в заглушках имели неправильные сигнатуры или отсутствовали вовсе. Без этого на AVX1-железе нельзя было даже запустить тесты.
ПОТОК ИСПРАВЛЕНИЙ
─────────────────
До: fused_up_gate ──► [missing in dispatcher] ──► garbage
После:
up_gate_exps
├── view_3d(gate) ─┐
│ ├──► mul_mat_id ─┐
└── view_3d(up) ───┘ ├──► fused_mul_unary(SILU)
◄──┘
Все операции уже имеют реализации без IQK/MatMul
Важный момент: патч ничего не меняет для AVX2-сборок. Всё обёрнуто в #if! GGML_USE_IQK_MULMAT. Результат идентичен оригиналу на современном железе.
Есть ещё один нюанс. Флаг --run-time-repack переупорядочивает квантованные веса в AVX2-специфичный формат Q8_0_R8. Это ломает вывод на AVX1 точно так же. Это отдельный баг, который в патч не вошёл — его просто отключили в скрипте запуска.
Результаты
Итоговые цифры на старом железе:
- Декодирование: примерно 5.2 токена в секунду
- Обработка промпта: около 16 токенов в секунду
- Формат модели: Q8_0 (восьмибитное квантование)
- Сборка: GGML_USE_IQK_MULMAT=OFF
Пять токенов в секунду — это не молниеносно, но вполне читаемо. Можно вести беседу. Можно генерировать код и ждать. Для бэкапа, когда платные API недоступны, или для пакетной обработки, где платить за каждый токен невыгодно, — вполне рабочий вариант.
Практический вывод
Что это значит для обычного разработчика?
LLM на CPU — уже не фантастика. Если у вас есть старый сервер, который пылится в углу, его можно оживить. ik_llama.cpp с патчем работает на любом Xeon поколения Ivy Bridge и новее. Десять лет назад такие машины стоили десятки тысяч долларов — сейчас их отдают за бесценок.
Да, скорость ограничена. Намного медленнее, чем GPU-инференс. Но это не про замену платным сервисам. Это про автономию: свой сервер, свои данные, ноль зависимости от того, что происходит с подпиской или API.
Для команды это может означать: один старый сервер как резервный inference-нод для критичных задач. Или выделенная машина для тестирования промптов, где не хочется гонять трафик через внешние сервисы.
Ещё один вывод: AI-ассистенты уже умеют чинить такие баги. Автор статьи — не C++-программист. Он ставил эксперименты, интерпретировал результаты, задавал вопросы. Весь анализ и патч сделал Claude. Это не «напиши за меня код» — это полноценное инженерное сотрудничество, где человек управляет процессом, а модель делает глубокую техническую работу.
Ссылки
- Оригинальная статья: Running Gemma 4 26B at 5 tokens/sec on a 13-year-old Xeon with no GPU
- ik_llama.cpp на GitHub — форк llama.cpp с оптимизациями для mixture-of-experts
- Патч с исправлениями (Pull Request #2138)
- Gemma 4 на Hugging Face — модель от Google
Дмитрий Полухин — продуктовый дизайнер. Пишу про разработку, AI и дизайн интерфейсов. Обо мне, контакты и профили.