Достучаться до небес — Корутины, Горутины и прочие Рутины
21.10.2017
Смотрели Достучаться до небес? Ахеренный фильм, всем советую. У меня всё. Или нет?
Итак, шёл вторник, 17 октября, обычный день. На хабре появилась крутая статейка — https://habrahabr.ru/post/339618/ — Послевкусие от Kotlin, часть 3. Корутины — делим процессорное время. Эта серия статей от независимого разработчика (он не из Jetbrains, пока), в которых рассматривается опыт использования Котлина в продакшене. Эта статья была конкретно посвещена Корутинам и их перфомансу.
В комментариях к статье я высказал своё мнение, что:
- Корутины от Котлина медленные (по сравнению с Горутинами).
- Котлиновские Корутины можно использовать лишь для натуральных асинхронных задач (например, загрузка превьюшек статей на мобильном приложении в бэграунде), а не для выполнения CPU-bound вычислений.
Меня в комментариях несколько опустили, сказав, что мои рассуждения неверны, и вообще я дурачок. Пришлось пойти в интернеты и почитать, как всё на самом деле.
В расследование вопроса производительности Котлиновских Корутин и Гошных Горутин я использовал 3 источника:
- https://github.com/Kotlin/kotlin-coroutines/blob/master/kotlin-coroutines-informal.md#implementation-details — официальное описание реалиции Котлиновских Корутин от создателей языка.
- https://groups.google.com/forum/#!msg/golang-nuts/j51G7ieoKh4/wxNaKkFEfvcJ — Ответ разработчиков языка Go, о том, как работают Горутины, и на сколько и почему они быстрые.
- https://stackoverflow.com/q/46864623/4167563 — ответ Романа Елизарова — одного из ключевых разработчиков Котлина, а также создателя Корутин в этом языке. Достучаться до небес, дотянуться до звёзд…
Благодаря этим ссылкам я получил конечную картину видения Котлиновских Корутин и Гошных Горутин.
Котлиновские Корутины:
- Это stackless корутины. Корутины реализованы с помощью компиляции вашего кода в Конечный автомат. Пример финального кода вы можете найти по ссылке на гитхаб. Всё состояние корутины хранится не на стэке (поэтому и stackless), а в Хипе. Для сохранения состояния используется специальный объект — Контекст. Пустая корутина занимает всего несколько байтов в Хипе.
- В Котлиновских корутинах возможна любая вложенность suspended functions — замореженных функций. То есть, из каждой новой корутины вы можете вызывать опять корутину, и так бесконечно. Переполнение стека не возможно. Причина — как раз таки Stackless. Состояние хранится в специальном объекте, а набор вызовов Замороженных функций сконвертируется в linked list этих самых объектов. Это сказал Роман, прочитать его слова можете по ссылке на StackOverflow.
- В котлиновских корутинах вы полностью сами управляете Пулом потоков, на которых раннаются таски. Как следствие, это даёт больше гибкости, но и можно написать херовый код, который забьёт всё CPU, и программа будет тратить всё время на thread context switch.
- В котлиновских корутинах вы сами решаете, где функция должна заморозиться. Это даёт больше гибкости опять-таки.
- Котлиновская Корутина — это Тупо набор независимых Таксов, отсортированный по порядку выполнения. Таски выполняются одна за одной. Каждая таска может выполниться на любом доступном Треде.
Гошные Горутины:
- Это stackful coroutines. Всё состояние хранится на стеке. Поэтому, если очень постараться, сделать милилард вложенных вызовов, то можно сделать себе переполнение стека.
- Нет возможности задать определенный пул потоков. Все горутины бегут в едином пуле, который определяетя самим языком. Пользователь лишь только может задать количество потоков этого пула. По умолчанию, число потоков совпадает с числом процессеров вашей машины.
- В горутинах нет возможности определять точки заморозки в функциях. Рантайм Го сам решает, где нужно передать выполнение от одной горутины к другой. Эти точки передачи вызовов определены в спеке языка. В частности, это места IO, а также блокировки на примитивах синхронизации.
- Переключение между двумя Горутинами — супер дешевое, O(1), то есть, не зависит от количества созданных горутин в системе. Всё, что нужно сделать для переключения — это поменять 3 регистра — Program counter, Stack Pointer и DX.
Что же лучше? Зависит, очень сильно. Нужно брать решения на обоих языках и мерить, по другому никак. В котлине пустые корутины более дешевые, чем в Го. Зато переключение контекста в Го — дешевле не порядок. Поэтому, если у вас будет часто выбираться новая Корутина для выполнения действия, то Го — ваш выбор. Если у вас возможен миллион вложенных вызовов — стоит посмотреть на Котлин, где гарантированно нет переполнения стэка.
Также стоит чётко подумать, IO-bound у вас задачи в Корутинах, или CPU-bound. Если первое, возможно, удивительно, но самое быстрое для вас будет — это исопльзование одного потока. И тут есть one thing. Котлин позволит создать набор корутин поверх Тред Пула из 1 потока, а в Го — вы так не сможете. Поэтому ещё раз, мерьте, не стесняйтесь.
Категории: О жизни