Короткий ответ: в описанном кейсе замена CUDA top-k fallback на radix-selection ускорила декодирование Qwen3-Flash-Next на двух RTX 3090 примерно с 30,2 до 33,3 токенов/с. Прирост составил 9-12% на каждом из трёх сидов при длинном контексте, MTP-3 и старом CUDA-сценарии, где top-k сортировал всю строку логитов.
Время отдельного top-k ядра сократилось с 5,1 до 0,25 мс на токен. Итоговая скорость выросла заметно меньше, поскольку декодирование включает вычисления модели, работу KV-кэша, обмен между двумя GPU и другие операции. Результат связан с конкретным профилем нагрузки и патчем из PR #28366. Подтверждённого выигрыша для prefill, режимов без MTP, других моделей и всех версий CUDA пока нет.
Коротко: патч ускорил декодирование, но только в проверенной конфигурации
Что именно стало быстрее
Radix-selection меняет путь вычисления top-k внутри CUDA. В контрольном варианте ядро тратило около 5,1 мс на токен, после замены алгоритма показатель снизился до 0,25 мс. Разница между значениями составляет примерно 20 раз для самой операции top-k.
Полный шаг декодирования ускорился с меньшим коэффициентом. Медианная скорость выросла примерно на 3,1 токена/с, с 30,2 до 33,3 токенов/с. Такой результат соответствует приросту около 10,3%, а заявленный диапазон по трём сидам составил 9-12%.
Эти цифры нельзя переносить на всю производительность llama.cpp. Top-k занимает лишь часть времени генерации. После получения логитов движку нужно выбрать следующий токен, обновить KV-кэш, выполнить операции модели и синхронизировать работу двух видеокарт.
Кому стоит читать дальше
Кейс относится к пользователям, которые запускают Flash-Next через llama.cpp на двух RTX 3090 и генерируют ответы после заполнения большого контекста. В тесте использовались длинные проектные документы, MTP-3 и GGUF UD-Q4_K_XL.
Практический интерес выше всего у тех, кто работает со сборкой на CUDA 12.0 или близким по поведению старым fallback-путём. Для коротких запросов, одной видеокарты, другой модели или запуска без MTP эффект может отличаться. Одинаковый результат для таких сценариев в описании кейса не заявлен.
Где llama.cpp терял время на top-k при старом CUDA fallback
Top-k нужен на каждом шаге генерации
При авторегрессионном декодировании модель получает логиты для следующего токена после каждого уже сгенерированного шага. Логит можно представить как числовую оценку кандидата. Top-k оставляет несколько кандидатов с наибольшими значениями, после чего сэмплер выбирает токен с учётом настроек генерации.
Операция повторяется для каждого нового токена. Потеря в несколько миллисекунд на одном шаге накапливается в длинном ответе, особенно когда модель генерирует большой объём текста или работает с уже заполненным контекстом. Поэтому небольшое CUDA-ядро способно повлиять на итоговые токены в секунду.
Почему сортировка всей строки избыточна
Согласно описанию кейса, старый CUDA 12.0 fallback полностью сортировал строку логитов. Такая операция строит порядок для всех значений, хотя top-k требуется только ограниченное число наиболее вероятных кандидатов.
Проблема здесь связана с ценой вычисления. Полная сортировка может корректно вернуть top-k, но выполняет работу, которая не нужна для остальных элементов. При длинном декодировании эта лишняя работа повторяется на каждом токене.
В узком смысле fallback не выдавал неправильный результат. Он использовал более дорогой путь для задачи, где достаточно определить верхнюю часть набора. Radix-selection сокращает именно этот лишний объём работы.
Как radix-selection выбирает top-k без полной сортировки
Селекция и сортировка решают разные задачи
Сортировка отвечает на вопрос: в каком порядке расположить все значения. Селекция отвечает на другой вопрос: какие элементы входят в верхнюю группу и где проходит граница top-k.
Если сэмплеру нужны, например, несколько самых больших логитов, полный порядок остальных значений не требуется. Алгоритм selection может найти нужную границу и передать кандидатов дальше без построения полной отсортированной строки.
Radix-selection использует последовательное сужение множества кандидатов по разрядам или битовым группам. На каждом проходе отбрасывается часть значений, которая уже не может попасть в top-k. Конкретные детали CUDA-ядра зависят от реализации, поэтому описывать их точнее без исходного кода и профиля сборки нельзя.
Почему ускорение ядра не превращается в 20-кратный рост токенов в секунду
Сокращение времени top-k с 5,1 до 0,25 мс действительно близко к 20-кратному ускорению этой операции. Полное время одного токена складывается из нескольких частей:
- вычисления слоёв модели;
- чтения и обновления KV-кэша;
- обмена данными между двумя RTX 3090;
- работы с логитами и сэмплирования;
- синхронизации и служебных операций движка.
Radix-selection сокращает один компонент задержки. Остальные участки шага продолжают работать с прежней стоимостью, поэтому общий прирост ограничен долей top-k в полном времени декодирования.
Результаты на llama.cpp на двух RTX 3090: 30,2 против 33,3 токенов/с
Что сравнивали
В описанном сравнении использовались два варианта top-k в llama.cpp при одинаковом основном профиле запуска. Контрольный путь применял CUDA fallback с полной сортировкой строки логитов. Второй вариант использовал radix-selection.
| Показатель | Контрольный вариант | Radix-selection |
|---|---|---|
| Медианная скорость декодирования | около 30,2 токенов/с | около 33,3 токенов/с |
| Время top-k ядра | около 5,1 мс на токен | около 0,25 мс на токен |
| GPU-конфигурация | 2 x RTX 3090 | 2 x RTX 3090 |
| Профиль нагрузки | длинный контекст, MTP-3 | длинный контекст, MTP-3 |
Прирост 9-12% повторился на каждом из трёх сидов. Формулировка «повторился» здесь описывает серию прогонов из исходного кейса, а не независимый бенчмарк AI-Manual.
Почему важны три сида и медиана
Один прогон может отклониться из-за фоновой нагрузки, состояния памяти, особенностей генерации или колебаний времени синхронизации GPU. Несколько сидов помогают увидеть, сохраняется ли направление эффекта при другой последовательности случайного выбора.
Медиана снижает влияние одиночного выброса. В этой серии трёх сидов достаточно, чтобы заметить устойчивый локальный эффект, но мало для широкого вывода обо всех видеокартах, версиях CUDA и моделях.
При самостоятельном сравнении полезно фиксировать не только среднюю скорость. Медиана, разброс между прогонами и время отдельного top-k ядра показывают картину точнее. О различии между prefill и decode с практической методикой измерения можно прочитать в разборе честного сравнения локального запуска Qwen 3.8 27B.
Конфигурация теста: что нужно учитывать при повторении кейса
Длинный контекст здесь не фон, а часть сценария
Лимит контекста в описанном запуске составлял 261 888 токенов. Измерения начинались примерно на отметке 119k токенов, поэтому цифры относятся к декодированию при уже заметно заполненном контексте.
Такой режим отличается от теста на коротком запросе. Размер KV-кэша, обмен между GPU и общая задержка шага меняются по мере роста контекста. При этом доступные данные не позволяют утверждать, что radix-selection даст больший или меньший процентный эффект на коротком промпте.
Для сравнения с другими настройками llama.cpp полезен разбор запуска Qwen 3.6 27B на RTX 5090, где отдельно рассматриваются длинный контекст, параметры батчей и спекулятивное декодирование. Аппаратная конфигурация и модель там другие, поэтому материал подходит для методики, а не для прямого сравнения скоростей.
MTP-3, expert-cache и f16 KV нельзя выносить за скобки
Исходные параметры задают конкретный профиль нагрузки. Их нужно фиксировать при повторной проверке, иначе разница между двумя сборками смешается с эффектом настроек модели и памяти.
| Параметр | Значение в описанном кейсе |
|---|---|
| Видеокарты | 2 x RTX 3090 |
| Модель | Qwen3-Flash-Next |
| Формат весов | GGUF UD-Q4_K_XL |
| KV-кэш | f16 |
| Expert-cache | 150 слотов |
| Спекулятивное декодирование | MTP-3 |
| Лимит контекста | 261 888 токенов |
| Начало измерения | примерно 119k токенов |
В исходном описании не указаны точная ревизия llama.cpp, драйвер, полный набор параметров запуска, частоты GPU, температурный режим и способ профилирования. Эти сведения могут влиять на воспроизводимость, поэтому повторение одной цифры без полного профиля даст слабое сравнение.
Проверка качества: нет явного ухудшения, но границы выборки важны
Что эти 238/240 и 235/240 позволяют утверждать
В проверке качества версия с radix-selection дала 238 правильных ответов из 240. Контрольная версия получила 235 правильных ответов из 240. Разница составила три ответа в пользу варианта с новым top-k.
В описанном кейсе разницу признали статистически незначимой. Один спорный вопрос не дал убедительного основания связывать результат с изменением алгоритма top-k. Поэтому эти числа показывают отсутствие явного ухудшения в конкретной серии, но не доказывают улучшение качества.
Radix-selection предназначен для ускорения выборки кандидатов. Он не добавляет модели новые знания и не повышает её способность рассуждать. Качество зависит от модели, квантования, промпта, параметров сэмплирования и самого набора вопросов.
Чего проверка качества не покрывает
Сравнение не подтверждает поведение за пределами примерно 119k токенов контекста. Неизвестно, сохранится ли разница на других задачах, датасетах, параметрах сэмплирования и длине ответа.
Результат нельзя автоматически переносить на запуск без MTP, другой формат GGUF, другую схему KV-кэша или иную конфигурацию GPU. Для рабочего применения нужна отдельная проверка на тех запросах, где ошибка выбора токена имеет практическую цену.
Стоит ли применять PR #28366 в своей сборке llama.cpp
Когда локальное сравнение оправдано
Проверка патча имеет смысл, если собственный запуск близок к условиям кейса:
- две RTX 3090 либо конфигурация с сопоставимой нагрузкой на GPU;
- Qwen3-Flash-Next или близкий сценарий Flash-Next;
- длинный контекст, начиная примерно со 100k токенов;
- декодирование с MTP-3;
- заметная доля времени на top-k;
- сборка CUDA, где сохраняется старый fallback с полной сортировкой.
Первое действие перед сборкой патча, проверить, какой путь top-k реально используется в текущей версии. Если узкое место уже закрыто другой реализацией или top-k занимает малую часть полного шага, прирост может оказаться ниже заявленных 9-12%.
Как сравнить версии без самообмана
- Зафиксировать одну модель, один файл GGUF, одинаковое квантование и одну ревизию рабочего сценария.
- Сохранить неизменными промпт, длину контекста, параметры сэмплирования, MTP-3, тип KV-кэша и настройки распределения по GPU.
- Прогнать обе версии после одинакового прогрева, чтобы первые операции загрузки не попали в измерение.
- Измерять prefill и decode раздельно. Скорость обработки входного контекста не заменяет скорость генерации токенов.
- Сделать несколько прогонов с разными сидами и записать медиану, минимальное значение и разброс.
- Сопоставить время top-k ядра с полным временем одного токена. Это покажет, действительно ли выигрыш связан с новым алгоритмом, а не с колебанием нагрузки.
Команды запуска в описании кейса отсутствуют, поэтому безопаснее повторять именно методику сравнения, а не копировать неполный набор флагов. Для честного результата нужна фиксация всей конфигурации, включая версию драйвера и параметры распределения модели.
Ограничения кейса: где выигрыш пока не подтверждён
Prefill и decode нельзя объединять в одну цифру
Заявленный прирост относится к декодированию, где top-k вызывается на каждом шаге выбора следующего токена. В доступном описании нет измерений prefill, то есть обработки входного контекста.
Отсутствие данных по prefill не доказывает отсутствие эффекта. Этот режим использует другой профиль вычислений, поэтому его нужно проверять отдельно. Нельзя прибавлять заявленные 9-12% к общей скорости обработки запроса без раздельных замеров.
Что нужно подтвердить первичными материалами до публикации
Для независимой проверки результата нужны:
- точный текст исходного кейса с таблицей прогонов;
- описание изменений из PR #28366 и ревизия llama.cpp;
- версия CUDA, драйвера и параметры сборки;
- метод профилирования top-k ядра;
- полная команда или набор параметров запуска;
- описание теста качества, промптов, сидов и настроек сэмплирования.
Пока эти сведения не собраны, цифры корректно описывать как результат конкретного кейса. Они дают сильный повод проверить radix-selection на близком железе, но не служат гарантией ускорения любой сборки llama.cpp.