Перейти к содержанию
Новое AiManual теперь в MAX Подписаться
Публикация AiManual

Почему 29x ускорение matmul дало лишь 6-10% прироста: разбор иллюзии оптимизации CPU-инференса

Разработчик ускорил ядро matmul в 29 раз, но инференс BitNet на Xeon стал быстрее лишь на 6-10%. Разбираем, почему модель упирается в пропускную способность DRA

Коротко

Что будет в материале

  1. 01

    Кейс: 29x в изоляции, 6-10% в реальности

  2. 02

    Почему matmul - не всегда узкое место: compute-bound vs memory-bound

  3. 03

    Архитектура BitNet b1.58 и её влияние на производительность

  4. 04

    Системное профилирование: как не попасть в ловушку «гонки за Gop/s»

Кейс: 29x в изоляции, 6-10% в реальности

Разработчик оптимизировал ядро матричного умножения (matmul) под ternary-модели BitNet b1.58 на процессоре Intel Xeon. Изолированный бенчмарк показал ускорение в 29 раз. Казалось бы, инференс должен взлететь. End-to-end тесты выдали прирост 6-10%. Причина - модель упирается в пропускную способность DRAM, а не в вычислительную мощность CPU.

Это классическое проявление закона Амдала. Ускорение фрагмента системы ограничено долей времени, которую этот фрагмент занимает в общем выполнении. Если matmul занимает 7% времени инференса, его 29-кратное ускорение даст максимум 6.8% общего прироста. Остальные 93% времени процессор ждёт данные из памяти.

Кейс вскрывает фундаментальную проблему CPU-инференса больших языковых моделей. Вычисления давно перестали быть узким местом. Главный тормоз - подсистема памяти. Пока индустрия гонится за терафлопсами, реальная производительность упирается в гигабайты в секунду.

Мы детально разбирали похожие эффекты в материале про тестирование Qwen3.5 122B на 64 ГБ RAM, где скорость генерации падала до 2.9 токенов/с именно из-за ограничений пропускной способности памяти при работе с большими моделями.

Почему matmul - не всегда узкое место: compute-bound vs memory-bound

Инференс нейросети находится в одном из двух режимов. Compute-bound - процессор не успевает считать, данные всегда доступны. Memory-bound - процессор простаивает в ожидании данных из памяти. Для больших языковых моделей на CPU второй режим является типичным.

Причина в арифметической интенсивности - отношении количества операций к объёму загружаемых данных (FLOP/byte). У стандартной матрицы весов FP16 каждый параметр весит 2 байта и участвует в двух операциях (умножение и сложение). Арифметическая интенсивность: 1 FLOP/byte. Пиковая производительность современного Xeon достигает 3-4 TFLOP/s, пропускная способность DRAM - 100-200 GB/s. Чтобы загрузить процессор на 50%, нужна арифметическая интенсивность 10-15 FLOP/byte. Модель с 1 FLOP/byte оставляет CPU недогруженным в 10-15 раз.

BitNet b1.58 усугубляет ситуацию. Тернарные веса {-1, 0, +1} упаковываются компактно, но арифметическая интенсивность падает ещё ниже. Умножение на -1, 0 или +1 - это условный знаковый сдвиг, вычислительная стоимость околонулевая. Модель становится экстремально memory-bound. Процессор тратит почти всё время на перемещение весов из DRAM в кэш.

Как определить, что ваша модель memory-bound

Диагностика сводится к трём шагам. Первый - рассчитайте арифметическую интенсивность модели. Разделите общее количество операций на объём параметров в байтах. Второй - сравните с отношением пиковой производительности CPU к пропускной способности памяти. Если арифметическая интенсивность модели ниже этого порога, она гарантированно memory-bound. Третий - используйте аппаратные счётчики. perf stat и Intel VTune показывают stalls на памяти, попадания в кэш и реальную утилизацию вычислительных блоков.

Для BitNet b1.58 на Xeon с 8 каналами DDR5-4800 расчёт выглядит так. Пропускная способность памяти: 8 × 38.4 GB/s = 307 GB/s. Пиковая производительность одного ядра в операциях над int8: ~200 GOPS. Арифметическая интенсивность модели: менее 0.1 FLOP/byte. Порог для полной загрузки ядра: 200 / 38.4 ≈ 5.2 FLOP/byte. Разрыв в 50 раз. Даже 29-кратное ускорение matmul не компенсирует этот разрыв.

Архитектура BitNet b1.58 и её влияние на производительность

BitNet b1.58 использует тернарные веса: каждый параметр принимает значение -1, 0 или +1. Это даёт экстремальную компрессию модели и теоретическую эффективность вычислений. На практике возникает парадокс: модель «лёгкая» в смысле вычислений, но «тяжёлая» в смысле работы с памятью.

Объём параметров BitNet b1.58-2B-4T составляет 2 миллиарда. Даже при упаковке в 2 бита на вес модель занимает около 500 МБ. Каждый токен инференса требует прогона всех весов через вычислительные блоки. Процессор вынужден прочитать 500 МБ из DRAM для генерации одного токена. При пропускной способности 307 GB/s теоретический потолок - около 600 токенов/с. Реальная производительность ниже из-за фрагментации запросов, латентности контроллера памяти и конкуренции с другими процессами.

Оптимизация matmul в этом сценарии напоминает тюнинг двигателя автомобиля, стоящего в пробке. Двигатель работает быстрее, но машина едет с прежней скоростью - узкое место в пропускной способности дороги. Процессор быстрее перемножает матрицы, но данные поступают с той же скоростью.

Движок Project Zero на чистом C99 демонстрирует альтернативный подход: вместо микрооптимизации одного ядра он перестраивает весь пайплайн инференса под особенности CPU. Результат - 36.25 токен/с на Xeon, что в 1.8 раза быстрее bitnet.cpp.

Системное профилирование: как не попасть в ловушку «гонки за Gop/s»

Гонка за гигаоперациями в секунду - типичная ошибка инженеров, переходящих с GPU на CPU. GPU с HBM имеют пропускную способность памяти 1-3 TB/s, и вычисления часто являются узким местом. CPU с DRAM на порядок медленнее, и узкое место смещается в память. Изолированные бенчмарки ядер создают иллюзию прогресса, которую end-to-end тесты безжалостно разрушают.

Правильный подход: сначала профилировать всю систему, найти самое узкое место, оценить максимальный теоретический выигрыш от его устранения, и только потом оптимизировать. Если бы разработчик из кейса сначала измерил долю matmul в общем времени инференса, он бы увидел цифру 5-8%. Даже бесконечное ускорение matmul дало бы максимум 8.7% общего прироста. Это сразу направило бы усилия на другие компоненты - квантизацию весов, оптимизацию доступа к памяти, увеличение ширины канала DRAM.

Roofline-модель для CPU-инференса

Roofline-модель - визуальный инструмент, показывающий потолки производительности. По оси X - арифметическая интенсивность (FLOP/byte), по оси Y - достижимая производительность (FLOP/s). Два ограничения формируют крышу: горизонтальная линия пиковой производительности CPU и диагональная линия пропускной способности памяти. Точка модели на этом графике сразу показывает, во что она упирается.

Для построения модели нужны три числа: пиковая производительность CPU в нужной точности (INT8, FP16, FP32), пиковая пропускная способность DRAM и арифметическая интенсивность модели. Точка BitNet b1.58 на Xeon лежит глубоко под диагональю памяти. Это означает, что любые оптимизации вычислений не дадут эффекта - модель жёстко ограничена скоростью DRAM.

Практический вывод: для memory-bound моделей на CPU нужно фокусироваться на уменьшении объёма передаваемых данных. Квантизация с 16 до 4 бит сокращает объём в 4 раза и потенциально увеличивает скорость инференса во столько же раз. Pruning убирает незначащие веса и уменьшает модель без потери качества. Увеличение количества каналов памяти (с 4 до 8) линейно поднимает потолок производительности.

Воспроизводимость на другом железе: когда оптимизация matmul окупится

Результаты кейса не универсальны. На GPU с HBM (например, H100 с 3.35 TB/s) пропускная способность памяти на порядок выше, и модель может стать compute-bound. Тогда 29-кратное ускорение matmul даст близкий к 29-кратному прирост end-to-end. Серверы с 12-канальной DDR5 и агрессивным чередованием (interleaving) также смещают баланс в сторону вычислений.

Ориентировочные цифры для разных платформ с BitNet b1.58-2B. Xeon 8-ch DDR5-4800: memory-bound, потолок ~600 токен/с, оптимизация matmul бесполезна. Сервер 12-ch DDR5-5600: граница memory/compute, потолок ~900 токен/с, оптимизация matmul даёт до 15% прироста. GPU H100: compute-bound, потолок ~5000 токен/с, оптимизация matmul критична. Apple M2 Ultra с unified memory 800 GB/s: memory-bound, но с меньшим разрывом, потолок ~1600 токен/с.

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

Практические выводы: как оптимизировать CPU-инференс с умом

Первый урок: всегда начинайте с end-to-end профилирования. Замерьте время инференса до оптимизаций, разбейте по компонентам (загрузка весов, matmul, attention, нормировки), найдите крупнейший потребитель. Второй урок: оцените арифметическую интенсивность модели и сравните с характеристиками железа. Roofline-модель за 15 минут даёт ответ, стоит ли оптимизировать вычисления.

Третий урок: для memory-bound моделей фокусируйтесь на уменьшении объёма передаваемых данных. Квантизация INT4 вместо FP16 даёт 4-кратное сокращение и пропорциональный прирост скорости. Pruning убирает до 50% весов без значимой потери качества. Четвёртый урок: не верьте изолированным бенчмаркам. Ускорение ядра в 29 раз впечатляет в отчёте, но 6-10% в продакшене - отрезвляющая реальность.

Правильный подход на кейсе BitNet выглядел бы так. Профилирование показывает, что 92% времени занимает чтение весов из DRAM. Арифметическая интенсивность 0.1 FLOP/byte при пороге 5.2 FLOP/byte подтверждает memory-bound. Вывод: нужно квантизовать модель до 2 бит (уже сделано в BitNet) и увеличивать пропускную способность памяти - больше каналов, выше частота, оптимизация размещения данных в NUMA-узлах. Оптимизация matmul - задача с низким приоритетом.

Разбор оптимизации инференса DeepSeek-V4-Flash на B300 демонстрирует тот же принцип на GPU: неожиданно низкая производительность объясняется отказом MoE-ядра и деградацией attention, а не недостатком вычислительной мощности. Системный анализ всегда важнее микрооптимизаций.

Подписаться на канал