Почему миллион корутин на Rust весит меньше, чем сто тысяч на Python
vibecodingai 9 минут назад Почему миллион корутин на Rust весит меньше, чем сто тысяч на Python Средний 6 мин 0 Rust * Системное программирование * Параллельное программирование * Python * Мнение TL;DR. async fn...
Anthropic — What company has the best second artificial intelligence model at the end of June?
В сфере искусственного интеллекта произошло заметное событие. vibecodingai 9 минут назад Почему миллион корутин на Rust весит меньше, чем сто тысяч на Python Средний 6 мин 0 Rust * Системное программирование * Параллельное программирование * Python * Мнение TL;DR. async fn компилируется в стейт-машину во всех языках, но кладут её по-разному. Rust генерирует enum фиксированного размера, который живёт на стеке вызывающего и не аллоцируется.
CPython заворачивает каждую корутину в объект на heap со ссылкой на фрейм. NET держит стейт-машину на стеке, пока выполнение синхронно, и боксирует её в кучу на первой приостановке. V8 после оптимизаций 2018 года не плодит лишних промисов на каждый await, но компактной стейт-машины на стеке у него по-прежнему нет: задача это промисы и реакции в куче.
Технические детали
Отсюда разница: Rust держит миллион корутин в одном процессе на сотнях мегабайт, а сервис на интерпретаторе упирается в память и аллокатор на порядок раньше. Ниже четыре рантайма под cargo expand, -Zprint-type-sizes, tracemalloc и sharplab, плюс замер Rust против Python на одной машине. Один и тот же по смыслу async-сервис: принять соединение, прочитать запрос, сходить в базу, ответить.
На Rust он держит сотни тысяч соединений и ест предсказуемую память. На Python ту же нагрузку приходится резать воркерами, потому что RSS растёт быстрее, чем число активных запросов, хотя «корутина же ничего не весит, это приостановленная функция». Счёт идёт не там, где ты смотришь: не в твоём коде, а в том, как рантайм хранит точку приостановки.
Чтобы перестать гадать, вскроем четыре рантайма и посмотрим, во что превращается твой await. async fn это стейт-машина. Это правда вездеВ прошлый раз мы разобрали, что async fn в Rust это enum.
Отраслевые последствия
Это утверждение в той или иной форме верно для всех языков, но расплата разная. Каждая точка await это вариант состояния, а локальные переменные, переживающие приостановку, должны где-то храниться. Вопрос один: где именно, на стеке вызывающего или в отдельной аллокации на куче.
Терминологически это деление на stackless и stackful корутины. Stackless (Rust, C#, Python, JS) хранят состояние в одном объекте фиксированного размера и не держат отдельный стек. Stackful (горутины в Go, фиберы) выделяют каждой задаче собственный сегмент стека.
На Хабре про эту дихотомию есть отдельный разбор (stackful vs stackless), здесь нас интересует stackless-ветка и то, куда именно ложится этот объект состояния в каждом языке. Rust: enum на стеке, ноль обязательных аллокацийВозьмём функцию с двумя точками приостановки:async fn handle(req: Request) -> Поставь cargo install cargo-expand и прогони cargo expand. Реальный вывод обёрнут в GenFuture и стейт-машину с дискриминантом, но если убрать шум, ядро выглядит так:enum Каждая .
Событие, по словам экспертов, усилит конкуренцию в сфере ИИ.



