Не все понимают, что тормозит инференс LLM. Сейчас расскажу — и, честно говоря, дальше вы уже сможете без меня. Почти вся оптимизация следует из этого. Всему виной то, что мы...
ГОНЯЕМ ВЕСА ТУДА-СЮДА
Совсем базово. В GPU, как и в других вычислительных устройствах, есть два класса памяти: DRAM и SRAM (хихихи, но не так смешно как JEPA у Лекуна).
DRAM — относительно дешёвая и относительно медленная. Именно туда вы загружаете модель, когда поднимаете инференс. Допустим, у вас модель на 35B параметров и вы храните её в fp16 (2 байта на вес). Значит, нужно 70 ГБ DRAM. Дальше разбираем на примере H100: там 80 ГБ. Влезло.
SRAM — дорогая и быстрая. Дорогого и быстрого логично давать умеренно. В 1000 раз меньше. Зато SRAM сидит вплотную к ядрам, на которых и происходят вычисления. Поэтому веса модели перетекают из DRAM в SRAM — на H100 со скоростью 3,3 ТБ/с.
Теперь самая тупая математика. Чтобы перетащить 70 ГБ весов со скоростью 3,3 ТБ/с, нужно 70 / 3300 = 21 мс.
Декодинг идёт токен за токеном, то есть ради каждого проклятого токена надо каждый раз прогнать через шину эти проклятые 70 гигабайт. Поздравляю, ваша максимальная скорость — 1 / 21 мс = 47 токенов в секунду. Прочувствуйте это. Мы еще ничего не считаем. При полной загрузки 70 ГБ весов вы никак не сделаете декодинг на 1 H100 быстрее 47 ток/секунду.
Это, мне кажется, действительно забавно. Вы купили за много миллионов рублей H100, которая умеет делать ОДИН КВАДРИЛЛИОН операций в секунду. Я даже гуглил это слово. А карта сидит холодная, потому что всё время уходит на перекачку весов. Это называется
Чтобы не быть настолько идиотом, возникает логичная идея. Раз я уж эти проклятущие веса из DRAM в SRAM перегнал — может, я обслужу не один запрос, а сразу несколько? Веса-то одни и те же. Чтобы H100 моя родненькая не стояла холодненькая.
Поздравляю, вы придумали батч. Если растить батч больше и больше, рано или поздно вы упрётесь уже в сами вычисления, в эти самые квадриллионы операций. Тогда вы попадёте в compute-bound режим.
Теперь реально понятно, как ускорять
— Квантизация. Храним вес не в 2 байтах, а в 1 или меньше — гонять через шину нужно вдвое меньше данных.
— Спекулятивный декодинг. Маленькая модель набрасывает несколько токенов, а большая проверяет их за один проход — то есть за одну прогонку весов.
— MoE-архитектура. Из памяти на каждом токене читаем только активные веса, а не всю модель (с батчом это работает хуже, обсудим потом).
И много чего ещё. И всё — вокруг одной и той же проблемы.
Часто, чтобы разобраться с кучей инженерных методов, надо понять всего один базовый принцип. Сегодня мы поняли его для инференса: ХВАТИТ ГОНЯТЬ ВЕСА ТУДА-СЮДА.
Теперь у вас точно всё получится ^^