shuttle_async: lock-free планировщик корутин на C++23 ускоряет задачи в 25х
PrimeCoder · 2026 · формат: tech
Разбор новинки простыми словами: что это, кому нужно и брать ли.
Показывает разработчикам, как бесстековые корутины C++23 и work stealing на деках Chase-Lev дают почти линейное масштабирование без мьютексов и Thundering Herd.
Связанные услуги: каталог, AI Boost Team, стать клиентом.
25.2х–26.2х на 31 потоке. Цифра звучит как рекламный баннер, пока не посмотришь, откуда она. Автор под ником shagunov26 выложил разбор shuttle_async — экспериментального планировщика задач на бесстековых корутинах C++23. Библиотека не продаётся и не продвигается: это изолированное вычислительное ядро для собственного движка ShuttleEngine на Vulkan API. И именно поэтому разбор стоит внимания — там показана механика, а не обещания.
Для владельца бизнеса здесь нет готового продукта. Есть сигнал: планка «нормальной» многопоточности в нативных проектах сдвинулась. То, что пять лет назад писали через пул потоков и мьютексы, в 2026-м упаковывают в lock-free планировщик на деках Chase-Lev. Если у вас в команде есть C++ и задачи с жёстким бюджетом кадра или отклика — это ваш текст. Если нет — читайте как маркер рынка: стоимость «правильной» асинхронности падает.
Что это такое, если убрать сишные детали
ShuttleEngine — графический движок на Vulkan. Vulkan снимает ограничения старых конвейеров и даёт контроль над GPU, но всю многопоточность на CPU перекладывает на разработчика. Автор формулирует прямо: рендерер приходится параллелить целиком — сборка командных буферов, стриминг текстур и мешей с диска в VRAM, физика, анимации, culling.
Разработка собственного графического движка на Vulkan API неизбежно упирается в многопоточность: параллельную сборку командных буферов, стриминг мешей и генерацию кадров.
Под это и написан shuttle_async. Внутри — бесстековые корутины C++23. Такие корутины по умолчанию «дикие»: они не привязаны к потокам ОС. Чтобы подчинить их игровому циклу, в рантайме есть абстракция IExecutor — экзекуторы решают, где и когда корутина получит управление. Переключение между корутинами идёт через Symmetric Transfer на уровне jmp-переходов, без обращения к планировщику операционной системы.
Чем это отличается от привычного пула потоков
Классический путь: std::jthread, std::mutex, std::condition_variable, очередь задач. В бюджет кадра 16.6 мс для 60 FPS (или 7.1 мс для 144 FPS) такой подход быстро упирается в ожидание блокировок. Мьютекс на критическом пути тянет за собой переключение контекста ядра ОС, а это в игровом цикле стоит непомерно дорого.
Второй классический враг — Thundering Herd. Одно уведомление condition_variable будит весь пул, и потоки начинают драться за атомики, сжигая CPU на пустую работу. В shuttle_async это лечат Coalesced Wake-ups: несколько пробуждений схлопываются в одно.
Третий слой — балансировка. Планировщик децентрализованный: у каждого воркера свой локальный дек Chase-Lev, плюс есть глобальная ConcurrentQueue. Загруженный поток не «зовёт на помощь» — свободные воркеры сами приходят и разбирают его дек. Мьютексов на критических путях нет вообще.
Что показали замеры
Стресс-тест собран в Release с -O3, без логирования и диагностики, чтобы мерить чистую пропускную способность. Железо — Intel Core i9-14900KF: 24 ядра, 32 потока. Три профиля нагрузки:
- Малые задачи. 50 000 корутин по 10 000 легковесных итераций. Проверка наносекундного оверхеда планирования и устойчивости очередей к драке за атомики.
- Большие задачи. 5 000 корутин по 4 000 000 тяжёлых итераций. Чистый compute: сколько сырой мощности кремния выжимает рантайм, включая разнородные P- и E-ядра.
- Неравномерные задачи. 10 000 корутин в хаотичной пропорции: 10% по 8 000 000 итераций, 90% по 50 000. Эмуляция реального кадра, где лёгкая логика сущностей перемешана с тяжёлым рендерингом и физикой.
Кривая масштабирования распалась на три зоны. До 8 воркеров — линейный рост, а на 4 воркерах в «малых» и «неравномерных» профилях ускорение перескочило математический предел: 4.40х и 4.05х. Объяснение физическое: корутины расходятся по независимым P-ядрам, их данные целиком укладываются в локальные L1/L2, обращений к общей L3 и RAM нет. На 16 воркерах ОС подключает Hyper-Threading на тех же физических ядрах, и рантайм держит 13.5х–14.5х. Дальше в игру входят E-ядра, и на 31 потоке система выходит на пик 25.2х–26.2х.
Кому это нужно и как применить
Первый адресат — небольшие команды, которые пишут свои движки, CAD, симуляции, обработку медиа на CPU. Везде, где есть кадр или запрос с дедлайном и много коротких независимых задач.
Второй — те, кто тонет в коллбэках:
Писать асинхронный код на цепочках коллбэков (Callback Hell) в 2026 году — инженерное преступление.
Практический вывод для руководителя: если в проекте асинхронность держится на мьютексах и ручных пулах потоков, узкое место часто не в алгоритмах, а в переключениях контекста и балансировке. Бесстековые корутины с work stealing — это способ вытащить производительность без переписывания логики задач.
Где подвох
Подвох первый — железо. Все замеры на одном гибридном процессоре Intel, и поведение там объясняется кэшами и тем, как Windows распределяет P- и E-ядра. На AMD, на серверных Xeon, на старых CPU картина будет другой. Переносить 25х на свой парк без собственного прогона — самообман.
Подвох второй — плато. Оно наступает уже на 32 воркерах:
На 32 воркерах наступает плато насыщения — это физический предел пропускной способности шины памяти процессора, когда миллионы атомарных операций в секунду упираются в возможности кремния.
Подвох третий — зрелость. Автор честно разбирает архитектурный баг синхронного инлайна, который едва не сломал инварианты Structured Concurrency. Это нормальный признак живой библиотеки, но и напоминание: такие ядра обкатывают годами. Плюс C++23 — не тот стандарт, который стоит на каждом проекте.
Брать или подождать
Брать — если вы пишете свой движок или CPU-bound сервис с дедлайнами и готовы держать в команде человека, который понимает корутины, атомики и memory ordering. Архитектурные идеи (локальные деки, Coalesced Wake-ups, отказ от мьютексов на горячем пути) переносятся и без копирования кода.
Подождать — если нужен готовый продакшн-компонент с гарантиями, SLA и поддержкой. Библиотека живёт внутри одного проекта, тестировалась на одной конфигурации, охват публикации на момент выгрузки — 789 читателей. Для бизнес-критичного контура этого мало.
Вердикт
Это инженерный текст, а не релиз продукта. Ценность — в разборе механики, которая объясняет, откуда берётся почти линейное масштабирование и почему мьютекс на горячем пути дороже, чем кажется. 25х на 31 потоке — личный рекорд автора на конкретном i9. Ваш результат будет другим, и узнать его можно только замером.
FAQ
Можно ли применить это в обычном бэкенде?
Идеи — да, код — с оговорками. Бесстековые корутины и work stealing полезны там, где много коротких задач и есть дедлайн. Для CRUD-нагрузки выигрыш будет в пределах шума.
Что такое деки Chase-Lev простыми словами?
Двусторонняя очередь без блокировок: владелец-поток быстро забирает задачи с одного конца, а чужие потоки аккуратно крадут с другого. Отсюда масштабирование без общей блокировки.
25х — это про любой код?
Нет. Это про три синтетических профиля на i9-14900KF в Release с -O3. Реальная нагрузка почти всегда грязнее.
А баг со Structured Concurrency критичен?
Автор его нашёл и разобрал. Для тех, кто берёт архитектуру как образец, это предупреждение: инварианты владения задачами проверяют тестами, а не обещаниями.
Если у вас в проекте есть узкое место на многопоточности — напишите в комментариях, какая нагрузка: короткие задачи, тяжёлый compute или смешанная. Разберём на цифрах, а не на лозунгах.
PrimeCoder Team · Официальный ответ · PrimeCoder
Опишите, кто сейчас обрабатывает заявки и какие SLA по факту — предложим поэтапный сценарий без остановки операционки.