Сообща на LLM. Масштабирование инференса

26.06.2025 | Серверы

 

Современные большие языковые модели (LLM) превратились в монстров, способных с легкостью загружать сотни тысяч графических адаптеров, тысячи юнитов серверных стоек, потребляя мегаватты электроэнергии. Монстры в обучении порождают меньших братьев – монстров логического вывода (инференса). "Большими" считаются модели на несколько сотен миллиардов параметров (DeepSeek-V3, 671 миллиард, из них 37 активных). В разряд "средних" попадают LLM с 70 миллиардами.

Специфика использования LLM требует достаточного быстрого (в идеале, в режиме реального времени) отклика – никто не захочет общаться с чат-ботом с задержками в минуты. А если приложение на базе LLM масштабируется до тысяч одновременных пользователей, формулировка выводов может потребовать больших кластеров графических процессоров.

Как работает масштабирование инференса с использованием нескольких GPU?

В 1967 году Джин Амдал сформулировал закон, обнаружив простое, но непреодолимое ограничение роста производительности при распараллелировании вычислений: «Если задача делится на несколько частей, суммарное время его выполнения на параллельной системе не может быть меньше времени выполнения самого медленного фрагмента».

Та часть программы, которую невозможно распараллелить, ограничивает общее ускорение. Любая крупная математическая или инженерная задача обычно состоит из нескольких частей, которые могут выполняться параллельно, и нескольких, выполняемых только последовательно. Эта связь описывается уравнением:

где:

  • S – теоретическое максимальное ускорение;
  • f — доля последовательных вычислений (которые нельзя распараллелить);
  • 1 – f – доля вычислений, которая может быть выполнена параллельно;
  • p – количество параллельных исполнительных элементов (процессоров, узлов и т.п.).

Прирост эффективности зависит от алгоритма задачи и ограничен сверху для любой задачи. Не всегда целесообразно увеличивать количество параллельных исполнителей (процессоров) в вычислительной системе: существуют накладные расходы (время, необходимое для передачи данных между узлами), а также ограничение масштабируемости . Начиная с определенного момента добавление новых узлов может даже увеличивать общее время расчета .

На рисунке изображено распределение условной программы машинного обучения на несколько параллельных устройств. Части программы, синие блоки, выполняются последовательно (получение данных, предварительная обработка запроса, инициализация параллельных узлов). Оранжевые блоки представляют вычисления, которые могут происходить одновременно (прохождение запроса через модель). Мы ограничены доступными узлами для одновременной обработки запросов. Если количество запросов превышает количество устройств, то они выполняются последовательно.

Становится понятным, почему при распределении трафика запросов на два графических процессора время прохождения не сокращается вдвое (в приведенном примере имеем ускорение на треть, S = 1.33). При параллельной обработке всегда будут последовательные операции и накладные расходы на распараллелирование.

Хотя любое ускорение является хорошей новостью, для оценки подлинной привлекательности объединения графических процессоров нужно учитывать дополнительные метрики инференса.

 

Задержка и пропускная способность

Это два фундаментальных показателя производительности, используемых для оценки вычислительных систем. Инференс не исключение.

Задержкой называют ожидание отклика, время, необходимое для прохождения одной единицы данных через систему от источника к месту назначения. В инференсе LLM это время между каждым следующим токеном, выдаваемым нашей моделью.

Пропускная способность количественно определяет скорость, с которой данные могут быть обработаны или переданы в течение заданного периода времени. Для нашего случая это количество выдаваемых моделью токенов в секунду. Более высокая пропускная способность указывает на возможность нашей системы эффективно обрабатывать больший объем запросов.

Эти два значения неразрывно связаны: уменьшение задержки обычно приводит к увеличению пропускной способности. Существуют способы их индивидуальной настройки.

 

Размер пакета

LLM состоят из многочисленных последовательных матричных умножений. При обработке на массово параллельных устройствах, таких как GPU, на обработку пакета запросов уходит немного больше времени, чем на прохождение единичного запроса (в определенной степени).

Если обрабатывать несколько входящих запросов одновременно, с низкими затратами на время выполнения, можно быстро умножать проходящие через нашу модель токены и выводимые из нее за единицу времени. На последовательном процессоре, таком как CPU, все шаги выполнялись бы последовательно, медленно.

Рисунок отображает сущность умножения матриц. Здесь A – пакеты входных данных, B – весы модели. Увеличивая размер входного пакета (размерность М матрицы входных данных А), мы можем ускорять инференс, увеличивая пропускную способность системы.

 

Квантование

В системе с ограниченной пропускной способностью много времени уходит на перемещение больших блоков данных для обработки, что приводит к очередям и узким местам. LLM обучаются с использованием 32-разрядной или 16-разрядной точности. Это означает, что каждый параметр модели представлен или 32 или 16 битами. Уменьшение размера данных – законный и эффективный инструмент для уменьшения задержки и повышения скорости при инференсе, в систему с заданной пропускной способностью. Квантование позволяет «засунуть джинна в лампу», обеспечивая возможность использования больших моделей на ограниченных ресурсах GPU. 32-битная модель требует 4 ГБ памяти для каждого миллиарда параметров, 16-битная – 2ГБ, 8-битная – 1ГБ и… LLM умещается в телефоне.

 

Кэширование

В основе LLM лежат трансформерные архитектуры, а их ключевым компонентом является механизм внимания (Attention Mechanism). В этом механизме для каждого токена (слова или части слова) последовательно вычисляются три вектора:

• Query (Q) – запрос

• Key (K) – ключ

• Value (V) – значение

Когда LLM генерирует текст, он делает это регрессивно, токен за токеном. Каждый раз, когда генерируется новый токен, модель должна "просмотреть" всю предыдущую последовательность (запрос и сгенерированные токены), чтобы определить, какой токен генерировать следующим.

При инференсе происходит много повторяющихся вычислений. Без KV Cache при генерации каждого нового токена модели приходилось перечислять векторы Q, K и V для всей уже существующей последовательности токенов - крайне неэффективно и медленно, поскольку объем вычислений растет квадратично с длиной последовательности.

Кэш – наш инструмент для уменьшения задержки и увеличения пропускной способности.

 

Источники оптимизации

В целом, у нас есть три параметра для оптимизации пропускной способности и задержки инференса LLM: размер пакета, квантование, кэширование. Квантование позволяет снизить требования к аппаратной составляющей – памяти GPU, и то до определенных пределов – ведь ответ на вопрос, сколько будет 2х2: «больше трех, но меньше пяти» нас вряд ли устроит. Два других параметра нуждаются в увеличении жизненного пространства, памяти GPU. Пакетам нужно пространство для работы.

Грубо размер модели можно рассчитать следующим образом:

Размер (в ГБ) = Параметры (в миллиардах) * Размер данных (в байтах)

Императив любого «инференсиста»: модель должна поместиться в память графического процессора, VRAM. Например, модель Llama2-13b с 13 миллиардами параметров с точностью fp32 имеет размер 52 ГБ, для fp16 – 26 ГБ. Такие аппетиты значительно ограничивают круг подходящих GPU. Более того, для больших пакетов и кэшей требуется дополнительный объем VRAM. Для модели с 13 миллиардами параметров один токен требует 1 МБ видеопамяти для «скользящих» вычислений. Если наш запрос из 128 токенов (примерно 90 английских слов или около 70 украинских) и мы ожидаем ответа на 128 токенов, то только это требует 256 МБ пространства. А если нас, любознательных, 50 душ, то 12 ГБ типичной видеокарты RTX5070 заканчиваются даже без загрузки модели.

Когда требуется увеличивать пропускную способность (т.е. размер пакета из-за сложности нашего запроса или количества обслуживаемых клиентов/запросов), нужна карта с большим объемом памяти.

Или распределение модели по нескольким GPU.

 

Стратегии многопроцессорного распределения

Простейший подход – повторение модели на нескольких графических процессорах. Модель полностью загружается в каждую карту, а система очередей распределяет запросы на каждую из моделей. Плюс – простота реализации. Минус – модель должна «влезать» целиком, VRAM используется нерационально (в каждом GPU одни и те же весы модели съедают память, уменьшая ее для «больших пакетов» и кэша).

Второй вариант – разбить нашу модель на части и разделить части между GPU. Этот способ значительно сложнее в «математике» и программировании, добавляет накладные расходы на взаимодействие – поскольку необходимо постоянно совмещать результаты вычислений перед переходом к следующему этапу/слою. Однако он позволяет использовать модели, значительно больше VRAM одного адаптера и работать с большими пакетами данных.

 

Реализации распределения

Параллелизм данных – это реализация стратегии повторения. Различные фрагменты данных обрабатываются на репликах нашей модели.

Параллелизм конвейера – это реализация стратегии разбиения, слои модели делятся между GPU. Это довольно простой подход: после того, как одно устройство завершило обработку своего фрагмента модели, промежуточные данные передаются следующему устройству. Разбить модель получится, а вот с параллелизмом возникнут проблемы – каждое устройство должно ждать завершения предыдущего, в результате большую часть времени GPU будут простаивать, ожидая друг друга.

Тензорный параллелизм – это тоже реализация стратегии разбиения, но вместо разбиения по слоям разбиваются «внутрислойные» большие матрицы.

Сходство умножения матриц и обычных чисел (за исключением коммутативности) позволяет упростить расчеты: части матрицы можно вычислять одновременно, а промежуточные тензоры объединять для получения полного результата. Таким образом, мы максимально загружаем тензорные ядра их любимой работой – умножением и сложением матриц, хотя возникают некоторые накладные расходы на синхронизацию тензоров.

 

Наблюдения практиков

Чтобы не было больно смотреть на ошибку Out of Memory модель должна помещаться в VRAM. Если модель содержится в памяти карты, распределение вычислений по нескольким GPU малоэффективно – транспортные расходы нивелируют параллелизм. Единственным оправданием является необходимость увеличения размера пакета. В этом случае можно использовать режим параллельной обработки данных на GPU даже больших, чем требуются модели.

Если модель не помещается в память одной карты, выбора нет, тензорный параллелизм в помощь. И здесь нужно понимать:

- выше «Амдаловского» ограничения не прыгнуть

– скорее всего задержка в «кластере» возрастет

- возрастет размер рабочего пакета, можно обслужить больше пользователей или расширить контекст

- экзотические методы пакетирования, не тот фреймворк, не в том месте, могут превратить кластер в тыкву.

Построение многопроцессорных решений на нескольких GPU – правильный и зачастую единственный путь, благо все открытые LLM поддерживают тензорный параллелизм. Достаточно широко применяется тензорный параллелизм вместе с параллелизмом данных.

Только полное понимание архитектуры приложений в широком контексте позволит правильно оценить требования к аппаратной части. Реализация требований в металле – если и не искусство, то чаще всего эвристический процесс.

По мотивам и с использованием материалов