Когда выбор следующего слова занимает больше времени, чем его осмысление
Вы запускаете LLM на своем сервере. Модель генерирует текст. Каждый новый токен - это мука. Процессор работает на 100%, вентиляторы воют как сирены, а скорость генерации - 2-3 токена в секунду. Знакомая картина?
Проблема не в матричных умножениях - их уже давно оптимизировали с помощью BLAS и SIMD инструкций. Проблема в самплинге - той самой процедуре выбора следующего токена из миллионов возможных вариантов. И вот здесь, в этой, казалось бы, простой операции, скрывался главный тормоз всей системы.
Top-K самплинг - это алгоритм, который выбирает K наиболее вероятных токенов из всего словаря модели (обычно 30-100 тысяч вариантов), а затем случайным образом выбирает один из них с учетом вероятностей.
Старый подход: сортировка миллионов чисел для каждого токена
Классическая реализация Top-K выглядела так: получить все логиты (необработанные "оценки" для каждого токена), применить softmax для преобразования в вероятности, отсортировать весь массив из десятков тысяч элементов, взять первые K значений. Каждый шаг - O(n log n) операций. Для словаря в 32,000 токенов это 32,000 операций сравнения и перемещения данных.
Теперь умножьте это на батч из 16 запросов. И на каждый сгенерированный токен. Получается адская арифметика, которая съедала до 40% времени инференса на CPU.
Новый подход: AVX2 и умная эвристика
Оптимизация, о которой идет речь, переворачивает подход с ног на голову. Вместо полной сортировки всего массива используется частичная сортировка с SIMD-ускорением. Вот как это работает:
- Используется AVX2 инструкции для параллельной обработки 8 float значений за такт
- Вместо полной сортировки - поиск K наибольших элементов через серию сравнений и перестановок
- Батчевая обработка: все запросы в батче обрабатываются параллельно
- Избегание лишних копий данных: работа напрямую с буферами логитов
Звучит просто? На деле это потребовало переписывания ядра самплинга на ассемблере с ручной оптимизацией под кэши процессора.
Цифры, которые заставят вас пересмотреть свои взгляды на CPU-инференс
| Конфигурация | Скорость до оптимизации (токен/с) | Скорость после оптимизации (токен/с) | Ускорение |
|---|---|---|---|
| Llama 3.1 8B, 1 поток, K=40 | 4.2 | 18.7 | 4.5x |
| Mistral 7B, батч 16, K=50 | 22.3 | 78.9 | 3.5x |
| Phi-3 Mini, 4 потока, K=30 | 15.8 | 121.4 | 7.7x |
| Большой батч (64 запроса) | 89.2 | 1874.5 | 21x |
Обратите внимание на последнюю строку. 21-кратное ускорение - это не опечатка. При больших батчах оптимизация раскрывается полностью, потому что накладные расходы на организацию SIMD-операций окупаются многократно.
Как это сравнивается с другими методами ускорения?
Есть TensorRT-LLM с AETHER-X, который обещает ускорение в 4.9 раза. Но он требует NVIDIA GPU последнего поколения. Наш метод работает на любом процессоре с AVX2 - а это почти все CPU за последние 8 лет.
vLLM-MLX показывает фантастические 464 токена в секунду на Apple Silicon. Но это специализированное решение для Mac. AVX2-оптимизация работает везде: от старых Xeon до новых Ryzen.
Главное преимущество - эта оптимизация не заменяет другие методы, а дополняет их. Вы можете использовать квантованную модель, MXFP4 формат, многопоточность - и поверх всего этого получить дополнительное ускорение от оптимизированного Top-K.
Важное ограничение: оптимизация требует процессора с поддержкой AVX2. Это Intel Haswell (2013) и новее, AMD Excavator (2015) и новее. Для старых CPU придется использовать fallback-реализацию.
Интеграция в ваши проекты: проще, чем кажется
Хорошие новости: оптимизация уже влита в основную ветку llama.cpp. Если вы собираетесь с нуля - она включится автоматически при обнаружении AVX2.
Если используете пребилды - проверьте флаги компиляции. Вам нужен -DLLAMA_AVX2=ON или -DLLAMA_NATIVE=ON (последний автоматически определяет возможности CPU).
Для тех, кто хочет максимальной производительности, рекомендую ручную сборку под свое железо. Разница может достигать 15-20% даже поверх этой оптимизации.
Кому это нужно прямо сейчас?
- Серверные развертывания на CPU: когда GPU слишком дороги или их нет
- Edge-устройства: промышленные компьютеры, IoT-шлюзы с x86 CPU
- Мультитенантные SaaS: где нужно обслуживать много пользователей одновременно через батчи
- Исследователи: которые экспериментируют с разными параметрами самплинга
- Энтузиасты с домашними серверами на старом железе
Особенно выигрывают сценарии с батчевой обработкой. Если у вас API, который принимает запросы от многих пользователей, эта оптимизация может сократить задержки в разы.
А что насчет будущего?
AVX2 - это 2013 год. Сегодня есть AVX-512, AMX (на Intel), матричные расширения на ARM. Оптимизация под эти инструкции может дать еще больший прирост.
Но главный потенциал - в специализированных инструкциях для самплинга. Представьте процессорную инструкцию, которая за один такт находит Top-K элементов в массиве. Звучит как фантастика, но именно так когда-то начинались SIMD-расширения для multimedia.
Пока же, если вы запускаете LLM на CPU, обновите llama.cpp до последней версии. Пересоберите с AVX2. Запустите бенчмарк. Разница вас удивит.
Иногда самые значительные улучшения приходят не от новых алгоритмов, а от того, чтобы наконец-то правильно оптимизировать старые.