← На главную

Photon от Moondream HQ прячет GPU bubble, ускоряя вывод на 35% на B200

30.06.2026 05:14 · hackernews

Когда GPU простаивает не из-за нехватки работы, а потому что CPU ещё не сказал ему, что делать дальше, это называют GPU bubble. В типичном цикле генерации текста модель выдаёт по одному токену за раз — это autoregressive процесс. GPU делает тяжёлую математику, но CPU тоже занят: выбирает запросы, настраивает метаданные, извлекает токен из выхода модели и так далее. Проблема в том, что GPU обрабатывает один токен очень быстро, а housekeeping на CPU — фиксированная стоимость каждого шага. GPU ждёт, пока CPU закончит — это и есть пузырь.

Photon от Moondream HQ прячет эти пузыри с помощью pipelined decoding. Идея — запустить GPU-работу над следующим токеном, пока CPU ещё заканчивает предыдущий. Механизмов три.

Первый — ping-pong slots. GPU для каждого шага нужен набор буферов: входные данные, выходные (logits), место для выбранного токена и служебная информация для KV cache. Буферы выделяются один раз, потому что выделение памяти может синхронизировать устройство и создать новый пузырь. Для перекрытия двух шагов нужно два независимых набора — DecodeSlot. Они чередуются: GPU считает на одном слоте, CPU читает результаты из другого. Копирование результатов на CPU идёт по отдельному stream, не блокируя следующий forward.

Второй механизм — forward now, sample later. В constrained decoding (например, когда модель должна выдать координаты точки, а не произвольный текст) разрешённые токены зависят от того, что уже сгенерировано. Но forward (вычисление logits) от этой маски не зависит — он может бежать вперёд. А sampling (выбор конкретного токена) ждёт, пока commit предыдущего шага обновит состояние. Так CPU успевает обработать прошлый шаг, пока GPU уже считает следующий.

Третий — zombies. Что делать, если последовательность на шаге t дошла до стоп-токена, но шаг t+1 уже запущен и включает её? Отменить GPU-работу нельзя. Photon помечает последовательность как finalized, но держит её в батче, пока на неё есть ссылки (inflight_refs). Она просто едет «мёртвым грузом» — занимает место, но не меняет состояние. Когда ссылки обнуляются, освобождаются KV cache и LoRA slot. Это дешёвый счётчик ссылок вместо сложной логики отмены.

Такая же pipeline работает и для prefill — обработки нового запроса с изображением. Photon не разделяет эти типы: prefill — просто ещё один запуск в той же двухслотовой системе. Это особенно выгодно для коротких ответов, где почти всё время уходит на prefill и приёмку.

Насколько это ускоряет? Всё зависит от скорости GPU. Чем быстрее GPU, тем больше доля пузыря. На NVIDIA 3090 при одном потоке прирост ~6.5%, на B200 при 32 потоках — уже 35%. Zombie tax (лишняя работа для закончивших последовательностей) заметен только при одном потоке — около 1%. В батче он почти исчезает, потому что стоимость шага определяется объёмом весов, а не числом строк.

Photon быстр не из-за одной этой техники. Ускорение складывается из десятков деталей по всему стеку — от обработки изображений до планировщика и синхронизации. Но pipelined decoding убирает главный тормоз: GPU перестаёт ждать CPU.

Читать оригинал →