После разлочки мощности видеокарты локальная Qwen3.8-27B давала 31 токен в секунду. Потом unsloth перевыпустила тот же квант по новой методике, я перешёл на свежий вариант — стало 35. И я считал это потолком.

Потом прикинул по пропускной способности памяти: карта отдавала 46% возможного. То есть половина простаивала.

Очевидный подозреваемый — Vulkan вместо CUDA. Я это уже пробовал, в те самые двое суток тупиков перед разлочкой мощности. Разницы не было, вопрос закрыл. Оказалось, зря.

Грабля первая: компилятор не знал мою карту

Тот замер был кривой. У каждой видеокарты NVIDIA есть номер версии набора инструкций — compute capability, его пишут как compute_90 или sm_90. Компилятор надо явно просить собрать код под нужную версию, а о версиях новее себя он попросту не знает.

Системный nvcc 12.4 умеет максимум compute_90 — это Hopper, серверные H100. А RTX 5080 Laptop — compute_120, потребительский Blackwell. Сборка работала через промежуточный PTX с трансляцией на ходу, без родных ядер. Мы с Claude Code мерили запасной путь и приняли его за CUDA.

Грабля вторая: установщик, который молча ждал Enter

Официальных сборок llama.cpp с CUDA для Linux нет вовсе, только для Windows. Значит собирать самому. Скачал установщик NVIDIA на 5 ГБ — и он молча повис, открыв крошечное окно X11 с просьбой нажать Enter. Нужен флаг --nox11.

А позже выяснилось, что установщик и не нужен: те же компоненты лежат отдельными архивами, и хватает 0.86 ГБ вместо пяти гигабайт.

Грабля третья: CUDA не собирается с новой glibc

CUDA 12.8 не собирается с glibc 2.43. Новая glibc добавила функции cospi, sinpi, rsqrt, а заголовки CUDA объявляют их сами, с другой спецификацией исключений. Правил руками (ну, руками Claude Code) шесть объявлений — этот же способ, как потом выяснилось, описан в документации llama.cpp.

Грабля четвёртая, самая опасная: замер, который врал

Собрал, замерил: CUDA даёт +20% генерации. Уже собирался переключать рабочие скрипты. Померил на настоящем промпте в 64 тысячи токенов — CUDA оказалась медленнее на 45%.

За день я поймал этот разворот трижды: спекулятивное декодирование через MTP давало +25% на коротком запросе и −12% на длинном, догадка по n-граммам +400% и −34%, черновая модель DFlash2 +29% и −43%. Любой короткий замер на этой задаче меняет выводы на реальной длине.

Грабля пятая: вывод, который мне не понравился

Вывод был такой: у CUDA нет быстрого пути для квантованного KV-кэша, остаёмся на Vulkan. Вывод мне не понравился, попросил копнуть ещё раз.

Оказалось: путь есть, ядро собирается по умолчанию. Медленно оно из-за конкретной ошибки, на которую уже есть отчёт — причём поданный на той же архитектуре и том же ядре Linux, что у меня, — и открытое исправление.

Что получилось

Собрал заново: CUDA 13.3, ветка с исправлением, родная архитектура sm_120.

Было Стало
Префилл на 64к токенов 555 tok/s 768 tok/s
Шаг агента 149 с 123 с
Стенд из 12 задач 12 мин 9 мин

Минус 21% времени на реальном шаге. Контекст при этом не пострадал — те же 196 тысяч токенов.

Потолок оказался подвесным

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

Несколько дней назад — 20.8 токена в секунду, и это был предел: карта работала на 80 ваттах из паспортных 175. Разлочил мощность — 31. Unsloth перевыпустила тот же квант по новой методике, с калибровкой под агентное программирование — та же модель, просто пересобранная, — перешёл на него, стало 35.

А CUDA прибавила не там, где я ждал. Генерация на коротком запросе выросла с 35 до 44 токенов в секунду, но на рабочем промпте в 64 тысячи токенов прибавка скромная: с 27.8 до 29.5. Зато префилл — обработка самого промпта перед ответом — ускорился с 555 до 768 токенов в секунду, почти на 40%.

И для кодинга это ценнее, чем прибавка к генерации. Агент читает много, а пишет мало: на 64 тысячи токенов промпта приходится около тысячи токенов ответа, три четверти времени уходит на чтение. Отсюда и минус 21% на шаге целиком.

Путь от 20.8 до 44 токенов в секунду и плюс 38% к обработке промпта — за несколько дней. На живых задачах шаг агента укладывается в две минуты вместо двух с половиной. И вот с этим уже можно работать, а не ждать у экрана.