Async: за кулисами абстракций
async/await в Python выглядят простыми — пока всё работает. За этими ключевыми словами скрываются событийный цикл, корутины и неблокирующий ввод-вывод, и понимание того, что происходит «под капотом», помогает писать асинхронный код осознанно, а не по шаблону.
Даниал разбирает асинхронность изнутри — от процессов и потоков до того, как из генераторов вырастает async/await.
В докладе: — процессы, потоки и адресное пространство: чем они отличаются и почему переключение контекста стоит дорого — конкурентность против параллелизма и кооперативная многозадачность — генераторы как основа корутин: send, yield и yield from — модели ввода-вывода: blocking, non-blocking и I/O-мультиплексирование (select, poll, epoll, kqueue) — как из этого складывается событийный цикл и почему Async = non-blocking I/O + мультиплексирование + кооперативная многозадачность — async/await как синтаксический сахар над генераторами
Презентация
1 / 49Текст презентации
Слайд 1: Async
Async За кулисами абстракций
Слайд 2: Кто я?
Кто я?
Слайд 3: Предисловие
Предисловие Данный доклад не является референсом к документации asyncio, в докладе будут рассмотрены типичные сценарии использования высокоуровневого API с некоторыми деталями реализации, за более обширными и конкретными юзкейсами стоит обратиться к документации. Цель доклада: дать правильный набор абстракций для вхождения в тему асинхронного программирования и пищу для дальнейших размышлений.
Слайд 4: Общая картина
Общая картина Порядок не совсем верный (или совсем неверный, решайте сами)
Слайд 5: Откуда ноги растут: процессы
Откуда ноги растут: процессы Процесс, если просто–исполняемая программа. У процесса есть свое адресное пространство, регистры (контекст) и состояние. (Виртуальное) адресное пространство процесса–диапазон адресов, которые выделены процессу. Эти адреса лежат в разных секциях/областях памяти, какие-то отвечают за код исполняемой программы, какие-то за глобальные неинициализированные данные и т.д.
Слайд 6: Иллюстрация процесса и его
Иллюстрация процесса и его адресного пространства. Адреса в адресных пространствах нескольких процессов ничего не знают друг о друге. Несколько процессов могут совместно использовать одно общее адресное пространство, такие называются потоками.
Слайд 7: Откуда ноги растут: потоки
Откуда ноги растут: потоки Поток–это абстракция ОС, которая может делить данные внутри процесса, грубо говоря–это те же процессы с общим адресным пространством. Внутри одного процесса может быть несколько потоков, которые используют одно адресное пространство (процессы так делать не могут, они независимы).
Слайд 8: Различие между однопоточным процессом (программой) и многопоточным
Различие между однопоточным процессом (программой) и многопоточным
Слайд 9: Какие проблемы решают?
Какие проблемы решают?
Слайд 10: Какие проблемы возникают
Какие проблемы возникают 1. Спавнить процессы–дорого (потоки дешевле, но на N задач это будет накладно) 2. Переключать контекст процессов и потоков–дорого, потому что нам нужно восстановить регистры 3. Планирование процессов и потоков–дорого. 4. Т.к. у потоков и процессов есть адресное пространство, на каждую единицу нужно выделить свой стек, кучу, регистры–дорого. 5. Блокировки и доступ к общей памяти–дорого и нетривиально в комплексных системах.
Слайд 11: Немного про переключение контекста
Немного про переключение контекста
Слайд 12: Конкурентность/параллелизм
Конкурентность/параллелизм Конкурентность связана с управлением несколькими задачами одновременно. Задачи могут начинаться, выполняться и завершаться в перекрывающиеся периоды времени, но не обязательно в одно и то же мгновение. В Python конкурентность обеспечивается при помощи Threading API и корутин. Параллелизм относится к одновременному выполнению нескольких вычислений. Это техника выполнения двух или более задач или вычислений одновременно, используя несколько процессоров или ядер в компьютере для выполнения нескольких операций параллельно. В Python обеспечивается при помощи multiprocessing и threading (с оговорками).
Слайд 13: Конкурентность/параллелизм
Конкурентность/параллелизм ByteByteGo on parallelism vs concurrency
Слайд 14: Виды многозадачности
Виды многозадачности Грубо говоря–задача сама говорит, в какой момент времени она засыпает и передает управление вызывающему коду. Такой подход называется кооперативной многозадачностью. Если задача сама не хочет отдавать управление вызывающему коду или не делает какие-либо системные вызовы, ОС (или другая вызывающая/исполняющая среда) может забрать управление самостоятельно, без участия задачи. (в контексте ОС–при помощи прерывания от таймера).
Слайд 15: Генераторы!!!
Генераторы!!! Генератор–реализация итератора, с методами send, throw и close. Сохраняет состояние и засыпает, когда код доходит до каждого встреченного yield.
Слайд 16: Бесконечная сумма
Бесконечная сумма
Слайд 17: Такие разные модели I/O
Такие разные модели I/O ● Blocking I/O ● Non-blocking I/O ● I/O multiplexing (a.k.a) I/O polling–select, pselect, poll, epoll, kevent, kqueue, IOCP proactor (windows) ● Signal-driven I/O (SIGIO) ● Asynchronous–AIO POSIX interface
Слайд 18: От userspace до kernelspace
От userspace до kernelspace
Слайд 19: От userspace до kernelspace
От userspace до kernelspace
Слайд 20: Файловые дескрипторы и сокеты
Файловые дескрипторы и сокеты
Слайд 21: Blocking I/O
Blocking I/O
Слайд 22: Синхронный блокирующий HTTP-клиент
Синхронный блокирующий HTTP-клиент
Слайд 23: Non-blocking I/O
Non-blocking I/O
Слайд 24: yield sock–”я подожду, пока в сокете появятся данные”
yield sock–”я подожду, пока в сокете появятся данные”
Слайд 25: Регистрация событий и добавление тасков
Регистрация событий и добавление тасков
Слайд 26: I/O multiplexing
I/O multiplexing
Слайд 27: I/O multiplexing
I/O multiplexing
Слайд 28: I/O multiplexing
I/O multiplexing
Слайд 29: Обработка событий и поллинг дескрипторов
Обработка событий и поллинг дескрипторов
Слайд 30: Запуск задач
Запуск задач
Слайд 31: Async = Non-blocking I/O + edge-triggered I/O multiplexing +
Async = Non-blocking I/O + edge-triggered I/O multiplexing + Cooperative multitasking Non-blocking I/O = fcntl/ioctl сисколлы + O_NONBLOCK/O_ASYNC флаги на файловых и/или сокет дескрипторах I/O multiplexing = select/pselect/poll/epoll/kqueue/kevent/etc. Cooperative multitasking = generators (корутины) (в контексте питона)
Слайд 34: Недостатки I/O multipexers
Недостатки I/O multipexers select: ● требуется 2 сисколла ● ограниченное кол-во файловых дескрипторов (1024) (можно поменять в FD_SETSIZE) ● временная сложность – O(n), где n–кол-во файловых дескрипторов ● Ядро каждый бегает по FD’s и регистрирует/перерегестрирует каждый–дорого epoll: ● Тяжело скейлить на треды: нужно использовать EPOLLEXCLUSIVE/EPOLLONESHOT флаги ● Используется 3 сисколла: epoll_wait, epoll_ctl, epoll_create ● Не умеет работать с обычными файлами The Linux Programming Interface book
Слайд 36: send(), или как в корутину передать значение
.send(), или как в корутину передать значение https://www.fluentpython.com/extra/classic-coroutines/
Слайд 37: Суммы, суммы, еще суммы
Суммы, суммы, еще суммы
Слайд 38: tiny tiny event loop
tiny tiny event loop https://github.com/megahomyak/tiny_event_loop/ blob/main/event_loop.py
Слайд 39: Fluent python. Ch. 21–asynchronous programming
Fluent python. Ch. 21–asynchronous programming
Слайд 40: yield from, или как вызывать вложенные корутины
yield from, или как вызывать вложенные корутины
Слайд 41: yield from, или как работает механика await
yield from, или как работает механика await https://www.fluentpython.com/extra/classic-coroutines/
Слайд 42: yield from, или как работает механика await
yield from, или как работает механика await
Слайд 43: async/await–сахар над генераторами
async/await–сахар над генераторами
Слайд 44: async/await–сахар над генераторами
async/await–сахар над генераторами
Слайд 45: async/await–сахар над генераторами
async/await–сахар над генераторами
Слайд 46: async/await–сахар над
async/await–сахар над генераторами
Слайд 47: Так когда же стоит использовать асинк?
Так когда же стоит использовать асинк? ● Для обработки мн-ва конкурентных сетевых соединений (не мн-ва RPS, см. C10K/C10M problem) ● Для межсервисного взаимодействия с непредсказуемым latency ● Для долгоживущих соединений (например, вебсокеты) ● Для медленных сетевых соединений ● Если удобная библиотека реализует асинк протокол
Слайд 48: Когда асинк использовать не нужно?
Когда асинк использовать не нужно? ● При работе с файловым I/O ● При работе с CPU-bound задачами ● При отсутствии экспертизы
Слайд 49: Дальнейшее чтение
Дальнейшее чтение ● io_uring, POSIX AIO API ● greenlets, libev/libevent библиотеки ● colored functions ● C10K/C10M problems ● back pressure a.k.a. flow control problem
Другие доклады митапа
- РС
- АГПрименение и оптимизация работы LLM. RAG, борьба с галлюцинациями Азамат Галимжанов
- ДТCelery — Best Practices Даурен Талгатулы














































