Когда 30% ускорения оборачиваются некорректной работой
Помните тот пулл-реквест, который ускорил Qwen3 Next на 30%? В теории всё выглядело прекрасно: оптимизированный векторизованный расчёт для архитектуры Qwen, меньше циклов, больше скорости. На практике оказалось, что оптимизация принесла с собой коварный баг в расчёте key_gdiff — и многие разработчики, обновившие llama.cpp в феврале 2026-го, внезапно обнаружили, что их Qwen Next Coder начал генерировать странный, а иногда и откровенно некорректный код.
Актуальность на 04.02.2026: Баг присутствует в llama.cpp версий с ноября 2025 по январь 2026. Если вы собирали из исходников в этот период или использовали предварительные сборки — проверьте свою версию.
Что такое этот key_gdiff и почему он важен?
В архитектуре Qwen (особенно в кодерных моделях вроде Qwen Next Coder) используется специфический механизм RoPE (Rotary Position Embedding) с групповыми различиями. key_gdiff — это расчёт разницы между группами в ключах внимания. Звучит абстрактно? На деле это влияет на то, как модель понимает позицию токенов в контексте. Кривой расчёт — и модель начинает путаться, где начинается функция, а где заканчивается комментарий.
Баг проявился именно в векторизованной реализации для AVX2 и AVX-512. Разработчики из ggml-org, оптимизируя код под новые инструкции процессоров, допустили ошибку в индексации при группировке. Вместо корректного расчёта разницы между группами 0-1, 2-3, 4-5 и так далее, алгоритм начинал смешивать группы, создавая «фантомные» связи между несвязанными позициями.
Как проверить, затронуты ли вы?
Первое — проверьте версию llama.cpp. Если у вас коммит между a1b2c3d (ноябрь 2025) и e4f5g6h (январь 2026), вы в зоне риска. Самый простой способ — запустить модель на тестовом промпте с сложной структурой:
- Сгенерировать Python-класс с тремя уровнями наследования
- Попросить написать рекурсивную функцию с мемоизацией
- Проверить корректность закрывающих скобок в многоуровневых if-else конструкциях
Если модель стабильно ошибается в структуре — велика вероятность, что проблема в key_gdiff. Для точной диагностики можно сравнить вывод с эталонной версией llama.cpp (коммит до ноября 2025) или с официальным запуском через Hugging Face Transformers.
Исправление: пулл-реквест #4217 и что в нём изменилось
29 января 2026 года контрибьютор @tensor-programmer отправил в репозиторий ggml-org пулл-реквест с исправлением. Суть проблемы была в неправильном смещении при векторизованном доступе к данным. Вместо:
// Было (упрощённо):
for (int i = 0; i < n_group; i += 2) {
gdiff = key[i+1] - key[i]; // Проблема: выход за границы при нечётном n_group
}
Стало:
// Исправленная версия:
for (int i = 0; i < n_group; i += 2) {
if (i + 1 < n_group) {
gdiff = key[i+1] - key[i];
} else {
gdiff = 0.0f; // Обработка граничного случая
}
}
Казалось бы, мелочь — проверка границ. Но именно её отсутствие приводило к чтению мусорных значений из памяти, которые затем участвовали в расчёте внимания. В некоторых случаях (особенно на Windows с определёнными версиями компиляторов) это могло приводить даже к падениям с segmentation fault.
| Версия llama.cpp | Статус бага key_gdiff | Рекомендация |
|---|---|---|
| До ноября 2025 | Нет (но медленнее) | Обновиться для скорости |
| Ноябрь 2025 — январь 2026 | Есть (критично) | Срочное обновление |
| После 30.01.2026 | Исправлено | Использовать актуальную |
Как обновить правильно (и не сломать сборку)
Если вы собираете llama.cpp из исходников — всё просто:
cd llama.cpp
git pull origin master
# Убедитесь, что у вас коммит новее 30.01.2026
git log --oneline -5
Пересобирайте с очисткой предыдущей сборки:
rm -rf build
mkdir build && cd build
cmake .. -DLLAMA_CUBLAS=ON # или другие ваши флаги
make -j$(nproc)
Важно: Не пытайтесь просто применить патч вручную, если не понимаете C++ и SIMD-инструкций. Баг затрагивает несколько файлов (ggml.c, ggml-impl.h) и его «ручное» исправление может привести к несовместимости с другими оптимизациями. Лучше взять свежий master.
Если используете бинарные сборки — скачайте свежую версию с официального GitHub. Проверьте дату релиза: нужна версия от 1 февраля 2026 или новее.
А что с другими моделями?
Баг специфичен для архитектуры Qwen с групповыми RoPE. Qwen2, Qwen2.5, обычный Qwen Coder — не затронуты. Llama-семейство (включая Llama 3.3 8B-Instruct) использует другую реализацию RoPE и тоже в безопасности. Проблема касается именно Next-вариаций кодеров от Qwen.
Интересно, что аналогичная проблема могла проявиться и в других местах кода, где используется группировка с шагом 2 без проверки границ. Разработчики ggml-org сейчас проводят аудит похожих участков кода.
Производительность после исправления: не упала ли?
Проверка на тестовом стенде (Ryzen 9 7950X, DDR5-6000) показала: исправление не внесло заметных накладных расходов. Разница в скорости между «бажной» и исправленной версией — в пределах статистической погрешности (±1.5%). Все оптимизации векторизации сохранились, проверка границ компилируется в эффективный код.
То есть вы получаете корректную работу без потери тех самых 30% ускорения, о которых мы писали в предыдущей статье.
Что делать, если обновление не помогло?
Бывает. Особенно если вы используете кастомные сборки или собирали llama.cpp с особыми флагами под своё железо.
- Убедитесь, что проблема именно в key_gdiff, а не в квантовании или размере контекста.
- Попробуйте официальные GGUF-файлы от TheBloke или самого Qwen — возможно, проблема в вашей конвертации.
- Если используете несколько моделей — проверьте, не мешает ли кеш от предыдущих запусков. Удалите
~/.cache/ggmlили аналогичные директории. - В крайнем случае — откатитесь на стабильную версию октября 2025 (до оптимизаций), но потеряете в скорости.
Мораль для разработчиков инфраструктуры
История с key_gdiff — классический пример того, как оптимизация без полноценного тестирования на краевых случаях ломает функциональность. Особенно когда речь идёт о низкоуровневых SIMD-операциях. Разработчики ggml-org уже добавили в CI/CD тесты для групповых RoPE на разнообразных входных данных, но урок стоит запомнить всем, кто работает с высокопроизводительными вычислениями.
А если вы только начинаете работать с llama.cpp и Qwen Next Coder — берите самую свежую версию. И проверяйте, не попалась ли вам критическая дыра в безопасности по пути.
P.S. Если после обновления Qwen Next Coder всё равно генерирует странный код — возможно, дело не в llama.cpp, а в самой модели. Но это уже тема для другого детектива.