MAATRIX / Блог / Пропускная способность памяти — настоящий потолок локальной LLM

Пропускная способность памяти — настоящий потолок локальной LLM

MAATRIX

Вы подняли модель на арендованном сервере, дождались загрузки — и она отвечает мучительно медленно, хотя nvidia-smi или htop показывают загрузку вычислительных блоков на жалкие 30-40%. Первый порыв — списать всё на «слабое железо» и искать карту помощнее. На деле генерация текста почти всегда упирается не в то, сколько операций в секунду умеет процессор или GPU, а в то, с какой скоростью железо успевает прочитать веса модели из памяти. Это другое узкое место, и лечится оно по-другому.

Почему генерация токена — это чтение памяти, а не вычисление

Языковая модель генерирует ответ авторегрессивно: токен за токеном, и каждый следующий токен зависит от всех предыдущих. Чтобы выдать один-единственный токен, вход должен пройти через все слои сети — через все матрицы весов модели целиком. Для обычной (не MoE) модели это буквально означает: на каждом шаге генерации нужно прочитать из памяти в вычислительные блоки все параметры модели, до последнего байта.

Здесь и зарыта суть проблемы. На шаге декодирования умножается огромная матрица весов на крошечный вектор — состояние всего одного токена. Вычислительно это дёшево: одно умножение и сложение на каждый вес. А вот трафика через шину памяти это требует ровно столько, сколько весит модель. Современные GPU и CPU способны выполнять в разы больше арифметических операций в секунду, чем нужно для этой одной операции на вес — поэтому вычислительные блоки простаивают, ожидая, пока по шине приедут очередные гигабайты.

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

Арифметическая интенсивность и формула потолка

В инженерии производительности это называется arithmetic intensity — отношение числа операций к числу байт, которые нужно переместить для их выполнения. У шага декодирования это отношение крайне низкое: на каждый прочитанный байт веса приходится буквально одно умножение с накоплением. Такая задача называется memory-bound — упирается в память, а не compute-bound — упирается в вычисления.

Отсюда прямое следствие для потолка скорости генерации:

время на один токен ≈ объём весов модели (байт) / пропускная способность памяти (байт/с)

и, соответственно,

потолок скорости (токенов/с) ≈ пропускная способность памяти / объём весов модели

Модель на 8 гигабайт весов и память, отдающая условные 400 гигабайт в секунду, теоретически даёт потолок около 50 токенов в секунду — не больше, независимо от того, насколько «мощный» процессор или GPU установлен. Конкретные цифры пропускной способности и реальной скорости на вашем железе я не привожу намеренно: они зависят от модели видеокарты, поколения памяти, драйверов и движка инференса, и правильный способ их узнать — измерить на своей связке, а не поверить чужому бенчмарку. Ориентировочно реальная скорость всегда ниже теоретического потолка — часть трафика уходит на KV-кэш, служебные буферы планировщика и накладные расходы движка, — но именно пропускная способность памяти задаёт верхнюю границу, выше которой прыгнуть нельзя никаким разгоном процессора.

Проверить фактическую скорость на своей модели просто: запустите генерацию с подробным выводом и посмотрите на eval rate. Если этот показатель уже упирается в потолок, дальше есть смысл разбираться в диагностике по шагам — этому посвящена отдельная статья про медленную генерацию токенов в Ollama.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Развернуть ИИ на сервере

Почему видеокарты с одинаковым числом ядер дают разную скорость

Из формулы выше следует практический вывод, который ломает интуицию «больше ядер — быстрее»: две видеокарты с похожим числом вычислительных блоков и близкими терафлопсами в рекламном листке могут выдавать заметно разную скорость генерации — если у них разная пропускная способность памяти.

Пропускная способность памяти видеокарты складывается из двух вещей: ширины шины памяти (сколько бит данных передаётся за такт) и типа/частоты самой памяти (GDDR6, GDDR6X, HBM и так далее). Карта с более узкой шиной физически не может прокачать столько же байт в секунду, сколько карта с широкой шиной, даже если у неё больше ядер CUDA и выше заявленная вычислительная мощность — потому что декодирование токенов упирается именно в этот параметр, а не в число ядер.

Практический вывод: при выборе видеокарты под инференс LLM в первую очередь смотрите на строку «Memory Bandwidth» в технической спецификации, а не на число ядер или терафлопсы FP32/FP16 из маркетингового листа. Для inference-нагрузки это гораздо более честный предиктор реальной скорости генерации, чем любая цифра «вычислительной мощности». Подробнее о том, как переводить это в выбор конкретной карты под конкретный размер модели, — в статье про выбор GPU-сервера для инференса LLM.

Квантование ускоряет генерацию не только экономией места

Квантование — снижение точности хранения весов (FP16 → INT8 → INT4 и ниже) — принято считать способом «влезть в память». Это верно, но это лишь половина эффекта. Вторая половина куда важнее для скорости: если веса занимают меньше байт, то на каждом шаге декодирования через шину памяти нужно прогнать меньше байт — а значит, при той же пропускной способности памяти генерация идёт быстрее.

Это прямое следствие той же формулы: числитель (пропускная способность железа) не меняется, а знаменатель (объём весов модели в байтах) уменьшается — и потолок скорости в токенах в секунду растёт. Грубо говоря, переход с 16 бит на 4 бита на вес сокращает объём трафика в память примерно в четыре раза — и именно поэтому квантованные модели ощутимо быстрее генерируют текст на одном и том же железе, а не только «помещаются» туда, где раньше не помещались.

Есть нюанс, о котором часто забывают: дéquantization (разворачивание сжатых весов обратно в рабочую точность перед умножением) добавляет небольшую вычислительную нагрузку, и на очень агрессивных схемах квантования или медленных реализациях эта накладная стоимость может съедать часть выигрыша. Кроме того, ниже определённого уровня сжатия начинает страдать качество ответов — выигрыш в скорости не бесплатен. Как выбрать конкретный уровень квантования под свою модель и железо, разобрано в статье про Q4/Q5/Q8 и что выбрать.

CPU против GPU: разная архитектура памяти, разная скорость

Раз узкое место — пропускная способность памяти, логично, что CPU и GPU дают принципиально разные результаты на одной и той же модели, и дело не в «слабости» процессора как таковой. У серверных CPU память организована в несколько каналов (два, четыре, шесть, иногда больше) DDR-памяти, разделяемых между всеми ядрами процессора и всеми задачами системы одновременно. У GPU память спроектирована ровно под одну задачу — максимально быстро кормить данными тысячи параллельных вычислительных блоков, поэтому используется принципиально более широкая шина и специализированные типы памяти (GDDR или HBM), которых просто нет в потребительских и большинстве серверных материнских плат для CPU.

Именно поэтому GPU почти всегда выигрывает у CPU в инференсе LLM даже при формально скромной вычислительной мощности — не потому что GPU «умнее считает», а потому что его память способна прокачивать байты кратно быстрее. Это же объясняет, почему добавление ядер CPU или переход на более дорогой процессор почти не ускоряет генерацию текста: увеличивается количество параллельных вычислителей, но не растёт пропускная способность общей шины памяти, в которую все они упираются. Если хочется разобрать эту развилку предметно — с конкретными сценариями, когда CPU-инференса всё же достаточно, — есть отдельный разбор CPU или GPU для локальной LLM.

Что проверять при выборе сервера под локальную LLM

Раз производительность определяется отношением «пропускная способность / объём весов», чек-лист при выборе конфигурации выглядит так:

  • Для GPU-сервера — смотрите характеристику Memory Bandwidth в спецификации карты (обычно указана в ГБ/с), а не число ядер или заявленные терафлопсы.
  • Для CPU-инференса — важно поколение памяти (DDR4 или DDR5) и число задействованных каналов; узнать реальную конфигурацию на сервере можно командой:
sudo dmidecode -t memory | grep -i "speed\|configured"
lscpu | grep -i "numa\|socket"
  • Соотнесите объём весов модели с пропускной способностью, а не только с объёмом VRAM/RAM — модель может «влезать» по объёму, но при этом упираться в шину и генерировать медленно.
  • Учитывайте несколько GPU отдельно — если модель разбита между несколькими видеокартами, часть трафика на каждом шаге идёт ещё и через межплатный интерконнект (NVLink, PCIe), и совокупная пропускная способность не складывается линейно так же просто, как объём VRAM.
  • Проверяйте скорость по факту, а не по спецификации — тестовый прогон с замером eval rate на вашей конкретной модели и квантовании даёт куда более честную картину, чем расчёт по паспортным цифрам, потому что на практике вмешиваются драйверы, версия движка инференса и загруженность системы посторонними процессами.

Где эта модель не работает: prefill, батчинг и длинный контекст

Формула «потолок = пропускная способность / объём весов» точна для одного пользователя, генерирующего ответ токен за токеном. У неё есть три важных исключения, которые стоит знать, чтобы не обобщать вывод неправильно.

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

Во-вторых, батчинг — обслуживание нескольких запросов одновременно — меняет расклад в лучшую сторону для сервера в целом. Если несколько пользователей одновременно ждут ответа, движок вроде vLLM может объединить их в один проход: одни и те же веса, однажды прочитанные из памяти, используются для вычислений сразу по нескольким последовательностям. Арифметическая интенсивность растёт, простаивающие вычислительные блоки начинают работать, и совокупная пропускная способность сервера в токенах в секунду на всех пользователей может быть заметно выше, чем скорость одного-единственного диалога, даже если задержка для конкретного пользователя не сильно меняется.

В-третьих, длинный контекст добавляет свою нагрузку на память сверх весов модели. Помимо самих весов, на каждом шаге генерации нужно читать ещё и KV-кэш — накопленное состояние всего предыдущего диалога, которое растёт вместе с длиной контекста. Чем длиннее история переписки или загруженный документ, тем больше байт добавляется к тому, что нужно прогнать через шину памяти на каждом шаге, — и скорость генерации на длинном контексте объективно ниже, чем на коротком, даже без единого изменения в самой модели.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Развернуть ИИ на сервере

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Правда ли, что видеокарта с меньшим числом ядер, но более широкой шиной памяти, обгонит формально «более мощную» карту в генерации текста?

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

Даёт ли увеличение объёма оперативной памяти сервера прирост скорости генерации?

Само по себе — нет. Объём памяти определяет, поместится ли модель вообще, но скорость определяется пропускной способностью (поколением DDR, числом каналов), а не количеством гигабайт. Больше памяти без роста пропускной способности решает проблему нехватки места, но не проблему скорости.

Почему при добавлении GPU в сервер скорость генерации не растёт пропорционально числу карт?

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

Стоит ли гнаться за самой дорогой видеокартой ради максимальных терафлопсов, если задача — просто чат с локальной моделью?

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

Как быстро прикинуть, во что упрётся моя конфигурация, ещё до аренды сервера?

Возьмите объём весов вашей модели в нужном квантовании (в гигабайтах) и разделите на заявленную пропускную способность памяти видеокарты или сервера из спецификации — это даст верхнюю границу скорости. Реальная скорость будет ниже из-за накладных расходов, но выше этого потолка она не поднимется никогда, независимо от процессора.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →