Fork llama.cpp под gfx906 может ускорить отдельные этапы инференса на Radeon VII, MI50 и MI60 за счёт выбора вычислительных путей, которые лучше соответствуют форме нагрузки старых AMD-ускорителей. Центральные изменения в заявленной теме связаны с переработанным small-Q Flash Attention, адаптивным выбором между native и convert-режимами и пересмотром прежних ускоряющих приёмов после перехода на DFlash2.
Практический эффект нужно оценивать по трём отдельным сценариям: first-batch prefill, заполнение длинного контекста и deep-context generation. Один удачный результат на prefill не доказывает ускорение генерации, а близкий показатель в одном режиме не отменяет пользы fork в другом.
В доступных материалах нет benchmark-логов, commit history и diff, по которым можно было бы честно назвать tok/s, проценты прироста или точные пороги переключения native и convert. Поэтому ниже отделены подтверждённые рамки задачи от технических гипотез, которые требуют проверки в исходном коде fork'а.
Короткий ответ: где fork llama.cpp для gfx906 действительно полезен
Fork имеет смысл проверять там, где upstream llama.cpp выбирает универсальный путь, а форма тензоров и стоимость преобразований на gfx906 делают другой вариант выгоднее. Наиболее интересны нагрузки с небольшим числом query-позиций, длинным уже накопленным контекстом и режимы, где Flash Attention тратит заметную долю времени на подготовку данных.
Это не универсальный переключатель производительности для всей llama.cpp. Выигрыш зависит от модели, квантизации, batch, длины последовательности, размещения слоёв и конкретного GPU. Для Radeon VII, MI50 и MI60 общий target gfx906 задаёт полезную точку сравнения, но не гарантирует одинаковый результат на всех трёх картах.
Какие изменения являются центральными
Карту изменений удобно разделить на три уровня. Первый касается small-Q Flash Attention, второй выбирает между обработкой исходного представления и конвертацией в другой layout или тип, третий проверяет, сохраняют ли прежние ускоряющие приёмы смысл после перехода на DFlash2.
| Изменение | Участок вычислений | Что проверяет benchmark |
|---|---|---|
| Переработанный small-Q Flash Attention | Расчёт attention при небольшом числе строк Q | Снижается ли стоимость запуска kernel и обработки малых матриц |
| Native и convert-режимы | Представление Q, K и V перед основным вычислением | Окупается ли конвертация преимуществом целевого kernel |
| Пересмотр старых ускорений после DFlash2 | Связка между подготовкой данных и основным вычислительным графом | Не добавляют ли старые шаги копирование, синхронизацию или лишний kernel |
Такое разделение помогает не смешивать причину и результат. Название патча ещё не говорит, что именно ускорилось. Нужны форма нагрузки, выбранный путь и замер времени на одинаковом наборе параметров.
Почему нельзя обещать одинаковый прирост во всех режимах
GPU обрабатывает разные формы матриц с разной эффективностью. Небольшой Q уменьшает объём полезной работы на один запуск kernel, поэтому накладные расходы и доступ к памяти могут занимать заметную долю времени. При большом Q вычисления становятся плотнее, и преимущество специального small-Q пути может исчезнуть.
На first-batch prefill размер Q зависит от длины входа и схемы пакетирования. При генерации для одной последовательности Q часто близок к единице, но объединение нескольких последовательностей меняет эффективную форму нагрузки. Заполнение длинного контекста увеличивает объём attention и давление на память. Deep-context generation сочетает маленький новый запрос с большим объёмом уже сохранённых K и V.
Поэтому корректный вывод звучит так: fork может быть быстрее в конкретной конфигурации. Формулировка «ускоряет llama.cpp на gfx906» без указания режима, модели и параметров запуска слишком широкая.
Почему gfx906 требует отдельного подхода в llama.cpp
gfx906 в этой теме выступает общей архитектурной целью для Radeon VII, MI50 и MI60. Это удобная база для анализа kernel-путей и сборки, но общий target не делает устройства полностью одинаковыми. Результат меняют объём доступной памяти, охлаждение, частоты, драйверный стек, распределение данных и способ запуска нескольких GPU.
Radeon VII, MI50 и MI60 в контексте одной архитектуры
Связь этих ускорителей через gfx906 полезна на уровне компиляции: разработчик может настраивать код под особенности одной архитектурной линии, а владелец карты получает понятную цель для проверки. Такой подход сокращает область поиска, но не заменяет тестирование конкретного устройства.
Сравнивать карты нужно минимум по четырём группам условий:
- аппаратная конфигурация: память, охлаждение и фактические рабочие частоты;
- программный стек: драйвер, ROCm и параметры сборки;
- модельная нагрузка: архитектура, формат, квантизация и размер контекста;
- режим работы: prefill, генерация, число последовательностей и размещение слоёв.
Если fork показывает лучший результат на одной карте, этот результат нельзя автоматически переносить на две другие. Особенно осторожно нужно трактовать сравнение Radeon VII с серверными MI50 и MI60: одинаковая архитектурная цель не убирает различия в окружении и режиме эксплуатации.
Что в upstream может быть не оптимально для старого AMD GPU
Upstream llama.cpp поддерживает широкий набор устройств и сценариев. Универсальный путь часто снижает сложность сопровождения, но может оставлять в коде преобразования, layout или размеры tile, которые не подходят конкретной форме нагрузки на gfx906.
Проблема может возникнуть в трёх местах:
- kernel получает слишком мало работы и не успевает загрузить GPU;
- данные перед вычислением приходится перекладывать или менять тип;
- несколько коротких операций получают больше накладных расходов, чем полезных вычислений.
Это не означает, что upstream llama.cpp работает неправильно. Его путь может быть лучшим для другого размера Q, другого batch или другой архитектуры GPU. Отдельный fork оправдан, когда diff и замеры показывают преимущество именно на целевой связке gfx906 и нагрузки.
Общий контекст сравнения AMD-бэкендов и изменений в llama.cpp приведён в разборе ускорения обработки промптов под ROCm. Его результаты нельзя переносить на этот fork без повторения условий, но методика разделения нагрузки и квантизаций здесь полезна.
Что изменилось после перехода на DFlash2
Переход на DFlash2 меняет не только название основного пути. Он может изменить порядок операций, форму промежуточных данных и долю времени, которую занимает подготовка тензоров. Из-за этого приём, который раньше экономил вычисления, способен превратиться в дополнительную работу.
В заявленной теме именно этот переход объясняет необходимость пересмотреть прежние ускорения. Без истории коммитов нельзя утверждать, какой конкретно патч стал нейтральным или отрицательным. Зато можно точно описать механизм, который нужно искать в diff и benchmark-логах.
Почему старая оптимизация перестаёт окупаться
Польза ускоряющего приёма зависит от баланса. Если он экономит большой объём вычислений, небольшой подготовительный шаг может окупиться. После смены вычислительного пути DFlash2 экономия может уменьшиться, а подготовка остаться прежней.
| Возможная причина | Что происходит | Как проверить |
|---|---|---|
| Лишняя конвертация | Тензор переводится в представление, которое новый kernel уже не требует | Сопоставить вызовы convert с путём native и измерить их время |
| Дополнительная синхронизация | GPU ждёт завершения короткой операции перед следующим шагом | Посмотреть порядок kernel в профайлере и интервалы между ними |
| Копирование данных | Экономия на вычислениях теряется из-за перемещения буфера | Проверить операции копирования и объём передаваемых данных |
| Неподходящий tile | Размер блока рассчитан под прежнюю форму матриц | Сравнить соседние размеры Q и граничные случаи |
Первые три пункта нельзя выдавать за описание конкретного fork без кода. Это проверяемые гипотезы. Факт появляется только тогда, когда соответствующий шаг виден в исходниках или трассировке и его влияние повторяется в замерах.
Как отличить нейтральный результат от регрессии
Разница между двумя замерами сама по себе ничего не доказывает. На результат влияют прогрев, фоновые процессы, частоты, состояние памяти и порядок запуска. Один случайный рост скорости не подтверждает ускорение, а один неудачный прогон не доказывает регрессию.
Для сравнения fork и upstream нужно:
- запускать одну модель с тем же форматом и квантизацией;
- фиксировать длину контекста, batch и число слоёв на GPU;
- разделять first-batch prefill, последующее заполнение и generation;
- прогревать обе сборки одинаково;
- собирать серию повторов и сравнивать медиану, разброс и выбросы;
- сохранять выбранный native или convert-путь, если программа его показывает.
Если статистики нет, корректная формулировка звучит как «результаты близки к паритету». Слово «регрессия» требует устойчивого ухудшения при одинаковых условиях.
Переработанный small-Q Flash Attention: зачем он нужен
Flash Attention выполняет attention блоками, ограничивая лишние обращения к памяти. В упрощённом виде путь работает с произведением Q и K, нормализацией результатов и умножением на V. Эффективность зависит не только от числа операций, но и от того, как эти блоки размещаются в памяти и насколько хорошо GPU загружен.
Почему размер Q меняет профиль нагрузки
Q в этом контексте означает число query-позиций, участвующих в текущем вычислении. Это не обозначение квантизации. При малом Q матрица запросов содержит мало строк, поэтому запуск крупного универсального kernel может оказаться несоразмерным объёму полезной работы.
При генерации одной последовательности новое вычисление часто содержит одну query-позицию, то есть Q близок к 1. Пакет из нескольких последовательностей меняет эту картину. При prefill Q может быть существенно больше, однако first-batch и последующие части обработки способны иметь разные формы из-за пакетирования и длины входа.
Малый Q влияет на четыре параметра:
- степень параллелизма внутри одного запуска;
- стоимость старта kernel относительно вычислений;
- число чтений и записей промежуточных данных;
- вероятность потерь на граничных размерах блоков.
Переработанный путь small-Q Flash Attention должен отвечать именно на эту проблему. Он не обязан быть лучшим для большого Q. Если kernel хорошо работает на одной строке запросов, это ещё не говорит о его поведении на плотном prefill.
Что именно проверять в реализации small-Q пути
Без diff fork'а нельзя назвать конкретные изменённые функции или размеры блоков. Технический анализ должен начинаться с пяти точек:
| Точка проверки | Вопрос |
|---|---|
| Выбор kernel | При каком Q включается специальный путь и есть ли отдельные случаи для Q=1 |
| Layout данных | Совпадает ли размещение Q, K и V с ожиданиями kernel |
| Типы данных | Появляется ли преобразование типа перед attention и где хранится результат |
| Синхронизация | Есть ли ожидание между подготовкой и вычислением |
| Граничные размеры | Как код обрабатывает Q, не кратный размеру tile |
Каждый пункт нужно связать с наблюдаемым результатом. Например, рост времени на Q=1 при наличии convert может указывать на высокую стоимость подготовки, но без профилирования это остаётся предположением. Переход на small-Q путь должен подтверждаться логом, трассировкой или воспроизводимым сравнением.
Адаптивный выбор native и convert-режимов
Native и convert описывают два способа подать данные в Flash Attention. Native-путь работает с исходным представлением, если выбранный kernel умеет его обработать. Convert-путь сначала меняет layout или тип данных, после чего запускает другой kernel. Точный смысл этих режимов нужно сверять с кодом конкретного fork'а.
Когда native-режим может быть выгоднее
Native сокращает число промежуточных операций. Если исходное представление уже достаточно удобно для kernel, конвертация добавит задержку и потребует дополнительной записи в память.
Преимущество native вероятнее при малом Q, коротком времени основного вычисления и частом повторении attention-шагов. В таких условиях фиксированная стоимость подготовки заметнее в итоговом времени. Это техническая логика выбора, а не подтверждённый benchmark-результат конкретного fork'а.
Проверять native нужно по двум показателям: времени всего attention-пути и доле времени, которую занимает подготовка. Быстрый основной kernel не даёт выигрыша, если перед ним выполняется дорогая конвертация.
Когда convert-режим оправдан
Конвертация может окупиться, если целевой kernel обрабатывает преобразованные данные намного эффективнее. Такой вариант особенно интересен при более плотной матрице Q, повторном использовании буфера или большом объёме вычислений после подготовки.
Convert не стоит считать недостатком автоматически. Его нужно сравнивать как сумму трёх частей: время преобразования, время основного kernel и дополнительные операции синхронизации. Сравнение только второй части даёт искажённую картину.
Конкретный сценарий, где convert лучше native, можно называть подтверждённым лишь после замера в fork'е. Переданные материалы не содержат такой таблицы, поэтому точные размеры Q, длины контекста и параметры batch здесь не подставляются.
Как работает адаптивное переключение
Адаптивный выбор должен связывать режим с измеримыми свойствами нагрузки. Минимальный набор кандидатов выглядит так:
- размер Q и число последовательностей;
- длина K и V, то есть глубина уже накопленного контекста;
- размер head и другие размеры тензоров;
- тип данных и схема квантизации;
- стоимость конвертации на конкретном backend;
- граничные случаи, где tile заполняется не полностью.
В исходниках это может быть условие, таблица порогов или набор эвристик. Точные значения нельзя угадывать по названию native и convert. Их нужно искать в коде выбора пути и сопоставлять с benchmark-строками.
Фактически выбранный режим проверяется только тем способом, который предусмотрел автор fork'а. Это может быть лог, benchmark-вывод или трассировка kernel. Если диагностического сообщения нет, нельзя приписывать проекту несуществующий флаг или переменную окружения. В таком случае остаётся анализ кода и профайлер.
Где fork выигрывает у upstream: разбор по рабочим сценариям
Сравнение fork и upstream нужно строить как таблицу нагрузок, а не как одну итоговую цифру. Для каждого режима фиксируются модель, формат и квантизация, длина контекста, batch, число GPU-слоёв, backend и версия программного стека.
| Сценарий | Что измерять | Почему результат может отличаться | Статус по доступным материалам |
|---|---|---|---|
| First-batch prefill | Время первого заполнения и throughput в токенах в секунду | Форма Q, запуск Flash Attention и стоимость подготовки данных | Числа fork и upstream не подтверждены |
| Заполнение длинного контекста | Скорость обработки нескольких длин контекста | Рост объёма K и V, работа с памятью и повторные конвертации | Набор длин и результаты не переданы |
| Deep-context generation | Latency одного шага и tok/s при уже заполненном контексте | Малый новый Q при большом объёме сохранённых данных | Измеримое преимущество не подтверждено |
First-batch prefill
Первое заполнение контекста создаёт заметный объём работы сразу. Здесь размер Q может быть достаточно большим, но конкретная форма зависит от входной строки и пакетирования. Если fork получил пользу за счёт small-Q, её нельзя автоматически ожидать на первом prefill.
Для честного сравнения нужно записать длину входа, batch, размер промежуточного batch, квантизацию и выбранный путь Flash Attention. Итог выражается через время обработки и tok/s. Значение без этих параметров неполно.
Отдельный замер first-batch особенно полезен для приложений, которые часто начинают новые запросы. Для диалога с длинной историей он не описывает основную задержку после загрузки контекста.
Заполнение длинного контекста
При росте контекста увеличивается объём данных, с которыми attention должен работать. Производительность может ухудшаться из-за пропускной способности памяти, роста числа операций и дополнительных преобразований. При этом характер деградации зависит от модели и backend.
Корректная таблица должна содержать несколько заранее заданных длин контекста, одинаковые настройки сборки и отдельную строку для каждой сборки. Нельзя сравнивать fork на одном контексте с upstream на другом и называть разницу эффектом кода.
Если скорость fork и upstream сближается при длинном контексте, это тоже полезный результат. Он показывает, что преимущество small-Q не перекрывает стоимость работы с большими K и V или что оба пути упираются в общий предел памяти.
Deep-context generation
Deep-context generation начинается после того, как большой контекст уже заполнен. Новая порция Q может быть маленькой, но K и V содержат длинную историю. Такой режим особенно хорошо показывает, как fork обрабатывает короткий запрос на фоне большого состояния attention.
Измерять нужно именно генерацию, без времени загрузки модели и первичного prefill. Полезны средний tok/s, latency шага и поведение после прогрева. Нельзя смешивать их с общей скоростью запроса, если задача состоит в оценке decode.
Связь между prefill и decode на длинном контексте хорошо видна в разборе сравнения рантаймов для GLM-5.3-Flash. Этот материал полезен как пример разделения метрик, но его цифры нельзя переносить на Radeon VII, MI50, MI60 или этот fork.
Почему паритет с upstream тоже важен
Паритет означает, что fork не ухудшает конкретную нагрузку, хотя и не даёт измеримого выигрыша. Для проекта, который меняет низкоуровневые kernel-пути, это ограниченный, но содержательный результат.
Если fork быстрее только на deep-context generation, его стоит выбирать владельцу системы, где преобладает именно такой сценарий. Для частого first-batch prefill преимущество должно быть подтверждено отдельным замером. При близких результатах на всех режимах upstream обычно остаётся более рациональной отправной точкой из-за широкой поддержки и меньшей зависимости от частных патчей.
Оценка fork должна отвечать на конкретный вопрос: какой режим получает выигрыш, при каких параметрах и какой ценой. Универсальная рекомендация без этих трёх ответов будет вводить в заблуждение.
Что нужно проверить перед запуском fork'а на Radeon VII, MI50 и MI60
Совместимость железа и программного стека
Сначала нужно проверить, какие устройства автор fork'а действительно заявляет, какой target используется при сборке и какой backend отвечает за запуск. Упоминание gfx906 не доказывает наличие готового бинарника или одинаковую поддержку всех трёх GPU.
Проверка должна охватывать:
- точную модель ускорителя и архитектурную цель сборки;
- версию ROCm, драйвера и компилятора, если они указаны в документации fork'а;
- требования к GPU-бэкенду llama.cpp;
- поддержку нужного типа данных и квантизации;
- ограничения, связанные с несколькими GPU и размещением слоёв;
- наличие известных регрессий после перехода на DFlash2.
Для AMD-систем нельзя отделять код llama.cpp от среды запуска. Изменение драйвера или параметров сборки способно изменить выбранный kernel и итоговую скорость. Практические ограничения ROCm и сопоставление разных backend разобраны в материале о запуске llama.cpp под ROCm, но перед запуском fork нужно сверяться с его собственной документацией.
Минимальный набор параметров для воспроизводимого сравнения
В журнале каждого прогона должны присутствовать одни и те же параметры:
- модель и её версия;
- формат файла и схема квантизации;
- длина входного и общего контекста;
- размер batch и внутреннего batch, если он используется;
- число слоёв на GPU и схема распределения между картами;
- backend, версия ROCm, драйвер и параметры сборки;
- режим Flash Attention и фактически выбранный native или convert-путь;
- число прогонов, прогрев и способ расчёта tok/s или latency.
Сравнивать нужно одну сборку fork с одной зафиксированной сборкой upstream. Обновление модели, драйвера и рантайма одновременно превращает тест в набор переменных, где нельзя выделить причину результата.
При отсутствии диагностики выбранного режима следует сохранить benchmark-лог и дополнить его профилированием. Не нужно добавлять в команду запуска неизвестные флаги: сначала проверяется список параметров конкретной версии и fork'а.
Когда разумнее остаться на upstream
Upstream остаётся рациональным выбором в четырёх случаях: целевой GPU не заявлен, нужный сценарий не измерен, fork собирается нестабильно или разница с upstream находится в пределах разброса.
Fork оправдан, когда одновременно выполняются три условия:
- устройство и программный стек совпадают с проверенной конфигурацией;
- рабочая нагрузка совпадает с режимом, где получен выигрыш;
- результат повторяется на одинаковых параметрах.
Если эти условия не выполнены, переход может добавить трудности сопровождения без практической пользы. Для домашнего AI-сервера стабильная upstream-сборка с близкой скоростью часто предпочтительнее экспериментальной ветки.
Ограничения выводов и как читать результаты без самообмана
Какие данные нельзя подставлять без первоисточника
По этой теме нельзя честно добавлять в статью или отчёт:
- проценты ускорения и конкретные значения tok/s;
- latency без описания длины контекста и batch;
- точные пороги выбора native и convert;
- названия релизов и версии ROCm без подтверждения;
- команды сборки и флаги запуска, которых нет в документации fork'а;
- характеристики Radeon VII, MI50 и MI60, если они не нужны для подтверждённой конфигурации;
- цитаты автора, пользовательский опыт и независимую проверку, которых нет в материалах;
- причинно-следственную связь между конкретным патчем и ускорением без diff или повторяемого benchmark.
Общие сведения о работе Flash Attention помогают сформулировать гипотезу. Они не заменяют данные конкретной сборки. Заявление «convert добавляет накладные расходы» описывает возможный механизм, а не доказанный результат для любого размера тензоров.
Численные результаты нужно подписывать условиями: GPU, модель, квантизация, контекст, batch, backend, версия сборки и тип измеряемой нагрузки. Без этого даже точная цифра плохо пригодна для сравнения.
Итог: кому fork может быть полезен
Fork llama.cpp имеет смысл рассматривать владельцам Radeon VII, MI50 и MI60, которым нужен локальный инференс на gfx906 и которые готовы проверить сборку на собственном стеке. Наибольший интерес представляют сценарии с small-Q, включая deep-context generation, но конкретное преимущество должно подтверждаться логами fork'а.
Переработанный small-Q Flash Attention может убрать потери на малых формах Q. Адаптивный выбор native и convert способен подобрать путь под размер тензоров и стоимость подготовки. Переход на DFlash2 требует перепроверить старые ускоряющие приёмы, потому что изменение вычислительного графа меняет цену конвертаций, копирований и синхронизаций.
Для first-batch prefill и заполнения длинного контекста нужны отдельные замеры. Если там fork близок к upstream, это ограничивает область рекомендации, но не делает ветку бесполезной. Практический выбор прост: совпали GPU, стек и рабочий сценарий, а результат повторился, fork можно использовать; нет подтверждения или появилась регрессия, оставайтесь на upstream до появления проверяемых данных.