Warning: Undefined array key 0 in /var/www/tgoop/function.php on line 65

Warning: Trying to access array offset on value of type null in /var/www/tgoop/function.php on line 65
- Telegram Web
Telegram Web
CPU, Memory Models, Concurrency, Multiprocess, Multithreading и Async. Часть 7. Модели ввода-вывода. Универсальная(блокирующая) модель ввода-вывода.

Все таки я поторопился с утверждением что следующий пост мы посвятим языкам программирования. Мы досконально разобрались с тем как реализовать параллелизм и конкурентность, но не разобрались с тем как устроены системные вызовы в ОС, в частности отвечающие за работу с вводом (input) / выводом(output).

Вернемся к вопросу производительности - из прошлых постов мы помним что наличие процессов и потоков позволяет программе выполняться эффективно. Эффективность достигается за счет того что операционная система управляет процессами и потоками, выделяя каждому время на исполнение кода.

Но есть ли в самом коде что-то что может повлиять на производительность программы? Да, есть. Это системные вызовы. Когда поток пытается выполнить операцию которой требуется обращение к ядру ОС (только с помощью него мы можем например попросить новую память или разрешение окрыть файл или соединение) он полностью останавливается и ждет ответа. В этот момент в случае многопоточной программы появляется возможность других потоков в рамках процесса делать полезную работу.

Если говорить о бэкэнд разработке основными системными вызовами являются операции чтения и записи во внешние устройства. В большинстве серверных приложений 90% времени посвящено именно работе с IO - обслуживание входящих соединений, запросы в СУБД, взаимодействие с third party провайдерами итп.

Давайте же разбираться что это за системные вызовы, рассмотрим базовый набор:
- open() - для открытия и создания файла. Возвращает файловый дескриптор (далее по тексту FD).
- socket() - создаёт конечную точку соединения и возвращает FD, указывающий на эту точку.
- read() - читаем данные. Аргумент функции - FD
- write() - пишем данные. Аргумент функции - FD
- close() - закрыть FD. C файловым дескриптором ассоциируется набор ресурсов, поэтому важно их подчищать после работы.

Это универсальный набор блокирующих системных вызовов из Unix, отвечающий за работу с внешними устройствами. Главной абстракцией во всем этом наборе является файловый дескриптор - универсальная сущность для организации ввода вывода любого характера. К слову этот пример демонстрирует красоту UNIX философии "всё есть файл"😊

Когда то давно разработчики были едины во мнении, что "блокирующего ввода-вывода хватит всем". Но те времена в далеком прошлом. Нагрузки увеличиваются, задачи усложняются и разработчики захотели лучше утилизировать железо, чтобы процессы и потоки прерывались как можно реже, ведь смена контекста это боль не только у людей но и машин.😃О том что из этой идеи вышло поговорим в следующем посте.


Спасибо что читали, буду рад вашей обратной связи в комментариях, до встречи в новых постах!
Евгений Козлов пишет про IT
CPU, Memory Models, Concurrency, Multiprocess, Multithreading и Async. Часть 7. Модели ввода-вывода. Универсальная(блокирующая) модель ввода-вывода. Все таки я поторопился с утверждением что следующий пост мы посвятим языкам программирования. Мы досконально…
CPU, Memory Models, Concurrency, Multiprocess, Multithreading и Async. Часть 8. Multiplexed IO

Давайте же разберемся какие улучшения были привнесены в ядра ОС для того чтобы повысить производительность при работе с вводом-выводом.

Начнем с мультиплексированного ввода-вывода. Cлово multiplexing переводится как «уплотнение». В чем же основные идеи, что будем уплотнять?
- Используем системный вызов "мультиплексор", он умеет в себя принимать НАБОР файловых дескрипторов и в момент когда один из них готов к работе происходит разблокировка и поток может начать читать данные.

Получаем 2 системных вызова вместо одного, может показаться что это скорее ухудшение ситуации, а не улучшение. Но это впечатление обманчиво. Мультиплексор возвращает контроль программе когда ХОТЯ БЫ один из файловых дескрипторов доступен для чтения. В случае когда у нас большое количество активных соединений у нас всегда будет кто-то доступный, с кем можно начать работу. Нам не нужны потоки на каждый коннект и блокировки а потом и переключения контекста чтобы начать обслуживать соединение.

По сути благодаря методу мультиплексору работа с набором файловых дескрипторов превращается в очередь "следующим будет тот кто готов".

Какие методы мультплексоры доступны разработчику?

- select(2). Самый первый и самый старый метод. Присутствует практически везде. Поддерживает возможность работать с 1024 файловыми декрипторами. Слабо подходит под задачи в нынешних реалиях (например тот факт что работать с файловыми дескрипторами проброшенными в select нельзя работать из других потоков). Использовать его стоит только если есть понимание зачем, в других случаях лучше выбрать более современную реализацию.

- poll(2). Развитие метода select(2). Умеет работать с большим количеством дескрипторов (основное достоинство) + у него чуть более удобный API. Подходит для кроссплатформенных реализаций, так как присутствует в большинстве ОС

- epoll(2). Развитие метода poll в ядре Linux. Оптимиизирован под работу с большим количеством соединений, довольно сложен в плане написания кода, но в определенных сценариях позволит выжать из железа больше чем просто poll.

- kqueue(2). Развите метода poll но для BSD.

Что из этого нужно использовать разработчику?

Напрямую - ничего 🙂 А если серьезно то большинство опенсорс проектов используют для работы с IO задачами специальные библиотеки которые предоставляют единый API для работы с любой из реализаций указанных выше. Наиболее популярные библиотеки:
- libevent (https://libevent.org/). Она используется в Chromium / Tor / PgBouncer и многих других.
- libuv (https://github.com/libuv/libuv). Поверх нее сделан NodeJS.

Теперь кажется мы точно разобрались со всеми внутренностями и можем пойти дальше. Хотя стоп, мы не затронули последний термин из заголовка нашего цикла статей - асинхронность. Что же это за зверь такой? И почему многие библиотеки называющие себя асинхронными под капотом используют то что называется "мультиплексированным вводом / выводом"?

Существует ли в реальности асинхронный ввод-вывод? Найдем ответы на эти вопросы в следующем посте😇

Спасибо что читали, буду рад вашей обратной связи в комментариях!
Всем привет!

Завтра (16.09) в 20:00 - обсуждаем clickhouse

В гостях будет Евгений Козлов - руководитель платформенной команды в продукте Statist, пишет посты о программировании и технологиях в телеграм канале.

Statist - единая платформа Т‑Банк для сбора продуктовой аналитики и телеметрии с мобильных и веб-приложений.

Подробнее о продукте - https://www.tbank.ru/career/technologies/statist/

Список материалов для знакомства с clickhouse:

Обзорная статья о главном движке для хранения данных в ClickHouse

Документация MergeTree

Углубленный разбор MergeTree (Слияния, Репликация)

Классная репа с преднастроенными конфигурациями клика и датасетами, мастхэв чтобы быстро что-то хочется поднять и поиграться
Всем привет! Я наконец вернулся из отпуска и у меня для вас приготовлена важная новость😇

-----

16 октября приглашаю Вас на Platform Engineering Night от Т-Банка — встреча для всех, кто создает платформы для инженеров. В Т-Банке довольно большое комьюнити платформенных разработчиков (к которому я тоже отношусь) и нам есть чем поделиться😊

На ней будут обсуждаться вопросы как повысить продуктивность с помощью инструментов, таких как Space и DevEx, а также поговорим о плюсах и минусах локальных решений и стратегиях импортозамещения.

Кроме будет практическая сессия по Event Storming: команда Sage покажет, как оптимизировать бизнес-процессы с помощью визуального моделирования. Также вы сможете познакомиться с нашими собственными платформами и сервисами.

И особенно важное событие лично для меня - на встрече будет стенд продукта над которым работаю я - Statist. Лично я поприсутствовать не смогу, но уверен мои коллеги расскажут вам о том как у нас всё устроено😊

📆 Время и место: 16 октября в T-Space, Москва, Грузинский вал, 7. Регистрация.

Делитесь приглашением с коллегами!
Евгений Козлов пишет про IT
CPU, Memory Models, Concurrency, Multiprocess, Multithreading и Async. Часть 8. Multiplexed IO Давайте же разберемся какие улучшения были привнесены в ядра ОС для того чтобы повысить производительность при работе с вводом-выводом. Начнем с мультиплексированного…
CPU, Memory Models, Concurrency, Multiprocess, Multithreading и Async. Часть 9. Asynchronous IO

Пора провести промежуточный итог для цикла статей. И закончим мы его как и планировалось - поиском ответом на вопрос: что же значит термин Async IO с точки зрения OC?

Но чтобы дать ответ на этот вопрос мне потребуется сделать несколько шагов назад и рассказать о том как исполняются программы с точки зрения ОС.

Программа может находиться в 2х режимах - Режим пользователя (User Mode) и Режим Ядра (Kernel Mode).

Если говорить очень простым языком - первый это когда программа выполняет код написанный разработчиком (циклы, функции, вычисления итп). Второй же нужен когда нам нужно обратиться к системным ресурсам (работа с файлом, сетевым интерфейсом). В режим ядра программа попадает при работе с системными вызовами (всеми теми разобранными в постах ранее).

Взаимодействие с ядром в большинстве случаев происходит в синхронном режиме и является блокирующим. То есть поток исполнения инструкций нашей программы остановится ожидая от ОС результатов вызова функции.

И вот это узкое горлышко очень хотелось оптимизировать разработчикам ОС, и для этого было предпринято ряд приседаний:
- для системных вызовов в Linux добавилась поддержка работы в неблокирующем режиме. В таком случае на плечи программиста будет ложиться ответственность за написание кода который будет повторять например операцию чтения пока в сокете не появятся байты так как ОС никак не уведомит программу что данные пора читать. Но вызов сам по себе максимально быстрый, особенно если перед этим мы убедились с помощью select / epoll что в сокете данные уже есть.
- aio(7) - поддержка настоящего асинхронного ввода-вывода. Если простым языком то системные вызовы также не блокирующие но изменяется механизм уведомления - ОС пошлет нам сигнал что пора бы начать читать данные из сокета. (Возможно здесь вы словили мысль "что-то мне это напоминает")
- io_uring - развитие идей aio (по итогу aio слили с io_uring). Умеет работать не только с сетью но и с файлами и блочными устройствами асинхронно. Один из самых современных подходов к разработке асинхронных программ. Биндинги к этому интерфейсу постепенно появляются в языках программирования.

Ок, тут мы разобрались, в линуксе есть системные вызовы, позволяющие делать настоящие асинхронные программы для задач связанных с IO. Но ведь большинство языков и программ используют врапперы над обычными epoll / select, во многих ЯП давно можно делать async / await или например создать promise и всё будет работать? Что это за магия?

Ответ: Из за отсутствия удобного API для асинхронного программирования на уровне OC стали появляться инструменты предоставляющие асинхронные абстракции в user mode (рантайм языка программирования) - Eventloop, reactor, fiber, coroutines, channels, futures, promises.

Благодаря этому разработчик может писать асинхронный код с точки зрения ЯП, оперировать понятиями асинхронного программирования, хотя под капотом с точки зрения ОС ничего асинхронного в программе может и не быть.

На этом всё, спасибо что читали, надеюсь было интересно и познавательно😇

Доп ссылки:
- What is io_uring?
- Does all system calls execute in kernel mode?
- Understanding asynchronous I/O; building an event loop from scratch
- Difference Between User Mode and Kernel Mode
- Linux fundamentals: user space, kernel space, and the syscalls API surface
Вижу по опроснику что надоели внутренности, пора переключаться на языки программирования. Но перед этим поделюсь с вами хорошим материалом дополняющим предыдущие посты.

Статья о том как эволюционировали I/O интерфейсы в Linux от select(2) до io_uring. С разбором плюсов / минусов и проблематики. Очень лаконично и понятно объяснили ребята)

https://sumofbytes.com/blog/understanding-asynchronous-in-linux-io-uring
Осваиваю новый вид активности, ставьте напоминалки в календарь, всех жду😇
Всем привет!

Стартуем новую книгу: Fundamentals of Data Engineering её можно бесплатно скачать по ссылке

Попробуем новый формат, проводить встречи будем вместе с Евгением Козловым

Обсуждаем первую главу в понедельник 28.10 20:00
Ранее я делал пост о конференции Platform Engineering Night от Т-Банка. Для тех у кого не получилось посетить подьехали записи выступлений на Youtube канале Код Жёлтый.

В том числе опубликована запись выступления СТО продукта над которым работает моя команда.

Как же так вышло, что мы делаем свою платформу и какие были варианты😊

https://youtu.be/-StrVahp51Y?feature=shared
CPU, Memory Models, Concurrency, Multiprocess, Multithreading и Async. Часть 10. Fibers и виды многозадачности.

Настало время погрузиться в то, как устроены примитивы исполнения на уровне ЯП. И первое что нужно сделать это установить терминологию.

Какие потоки исполнения существуют в рамках любой программы?
• Поток ОС (поток, предоставленный нам ОС)
• Аппаратный поток (фактический физический поток на ЦП)
• Fiber-поток (поток исполнения в нашей программе)

Fiber — это, по сути, обозначение в Computer Science любых легковесных потоков исполнения. Далее буду использовать это термин для обозначения любых рантайм потоков исполнения. Если говорить грубо, то fiber-потоками можно назвать корутины, горутины, тасклеты, зеленые потоки. У них у всех есть некоторые различия, но суть остается той же.

Ок, мы получили в свое распоряжение файберы, теперь нужно разобраться как в коде организовать мультизадачность, т.е. как программа будет вести себя если мы создадим несколько файберов. Рассмотрим 2 основных подхода, упомянутых ранее в постах про внутренности ОС:

🔹Кооперативная многозадачность - тип многозадачности, при котором фоновые задачи выполняются только во время простоя основного процесса и только в том случае, если на это получено разрешение основного процесса.

- Python (asyncio)
- JavaScript (async/await + Promise)
- Ruby (Fibers)
- Lua (coroutines)
- Java (virtual threads, JDK 21+)
- C++ (coroutines)

🔹 Вытесняющая многозадачность – тип многозадачности, в котором присутствует планировщик, обеспечивающий переключение между файберами. Каждый получает свой квант времени, в рамках которого делает полезную работу.

Реализована во всех языках в которых потоки исполнения являются реальными потоками OC и есть возможность создавать процессы.

- C/C++ (pthreads),
- Java без виртуальных потоков (java.lang.Thread)
- Node.JS (worker threads),
- Python(multiprocessing, multithreading)
- Ruby (fork, Thread)

Думаю, вы заметили, что одни и те же языки упоминаются в обоих абзацах. Что наводит на мысль что нет одного предпочтительного способа организации многозадачности, и что оба варианта можно и нужно комбинировать для достижения максимальной производительности.

Особенно внимательный читатель наверняка заметил, что нет упоминаний языка Go, и это не просто так. Дело в том что в Golang реализован гибридный подход к мультизаданчости и явно отнести язык в одну из категорий было бы неправильно.

На сегодня всё, в следующих постах будем изучать конкретные реализации многозадачности в языках программирования😇 Разберемся что там в Go, зачем нужен GIL, и попробуем разобраться как же правильно писать крутой конкурентый код зная всю ту базу о которой я рассказал ранее.

Спасибо за то что читали, до встречи!
Вчера произошло важное для меня событие - первый подкаст в роли гостя.

Вместе с Кириллом Мокевниным (Hexlet, Организованное программирование) потрещали о стартапах и больших корпорациях, многоуровневые собесы, внутренние платформы для разработчиков в больших компаниях и многое другое. Поделился своим профессиональным опытом и взглядом на вещи. Надеюсь вам будет полезно и интересно😊

Таймкоды и ссылки на другие подкаст-платформы в описании видео😇

https://youtu.be/Lo4F8NMmkN0
А ещё сегодня продолжаем читать Fundamentals Of Data Engineering в рамках книжного клуба - обсуждаем главу The Data Engineering Lifecycle.

Всех жду😊
https://www.tgoop.com/point_rar/457
Евгений Козлов пишет про IT
CPU, Memory Models, Concurrency, Multiprocess, Multithreading и Async. Часть 10. Fibers и виды многозадачности. Настало время погрузиться в то, как устроены примитивы исполнения на уровне ЯП. И первое что нужно сделать это установить терминологию. Какие…
CPU, Memory Models, Concurrency, Multiprocess, Multithreading и Async. Часть 11. Сравнение видов многозадачности

В прошлом посте было дано определение 2м основным видам многозадачности используемых в Computer Science. В этом посте хочется дать чуть больше пояснений и порассуждать где и почему они применяются.

Вытесняющая многозадачность реализована на уровне ОС, практически все системы которые я знаю реализуют подход с вытесняющей многозадачностью. Этот подход позволяет пользователю ощущать что все запущенные программы на компьютере работают параллельно (при наличии ядер процесссора, помним про SMP). А если не параллельно то как минимум конкуррентно, то есть каждой задаче выделяется ресурс "поработать" а дальше ОС останавлвает программу чтобы выделить ресурсы соседнему процессу.

Какие основные плюсы и минусы есть у данного подхода к мультизадачности:
- Плюс: невозможна ситуация когда ресуры системы голодают (starvation) в ожидании работы. Планировщик всегда найдет для процесса поток и ядро процессора для исполнения команд.
- Минус: наличие планировщика так или иначе дает оверхед.

Если переходить к кооперативности то тут основной плюс в том что переключениями может управлять разработчик (Ну или рантайм языка программирования, т.е нечто на уровне самой программы а не ядра ОС). И планировщика может и не быть вовсе. Но имея такую силу в своих руках можно получить ситуацию когда мы пишем кооперативный код который с легкостью заблокирует всю программу и не передаст возможность делать полезную работу дргим файберам в рамках процесса. По сути программа зависнет и придется ее перезапускать. Чуть менее критичная ситуация - starvation, когда файбер не передает ресурсы другим файберам хотя ему они сейчас не нужны (например он заблокировался на IO).

Так зачем вообще нужно использовать кооперативную многозадачность? Давайте использовать нативные треды операционной системы и не будем париться? За нас все сделает ОС. Такой подход вполне имеет право на жизнь, но он является достаточно дорогим с точки зрения ресурсов. Треды дороже с точки зрения памяти + ими управляет ОС и переключения между ними очень дорогие. При этом помним что при разработке серверных программ большое количество времени программа проводит в состоянии ожидания ввода-вывода и для эффективной утилизации ресурсов и комфортного пользовательского опыта важно чтобы во время простоя могли выполняться другие задачи на железе.

Для того чтобы программисты могли писать IO bound программы эффективнее разработчики языков и начали привносить в рантаймы различные легковесные примитивы. Но что произойдет в программе если мы сделаем блокирующий вызов (например запрос к БД)? Зависит от того как реализован рантайм конкретного языка программирования. Где то мы в ответ получаем Promise и продолжаем выполнение программы и заблочиться на нем в ожиданиии результата спустя время. В других языках реализованы специальные планировщики которые умеют детектировать файберы которые заблочились на IO и их можно безопасно снять с реального потока ОС и выделить время другим файберам. То есть по сути получаем некоторый микс из двух подходов, взяли лучшее из каждого.

А как быть с CPU Bound задачами? Никак, CPU задачи не масштабируются с помощью файберов. Чтобы разобраться почему приглашаю читать предыдущие посты с обсуждением процессора😊

На этом на сегодня всё, спасибо что читали, до встречи в следующих постах.😇
Есть мысли попробовать для разбора языков програмирования делать статьи или даже видео. В каком варианте вам было бы комфортнее?
Anonymous Poll
39%
Видео
58%
Статья
41%
Пост в канале
6%
Стрим, чтобы в лайве задавать вопросы
Вижу что вам комфортнее всего взаимодействовать с текстом. А подскажите в таком случае, что сейчас стильно / модно / молодежно для бложика использовать? Я последнее что использовал это Jekyll, но наверняка прогресс уже ушел сильно дальше. Поделитесь в комментариях, думаю доберусь и создам свой webpage😊
Учел ваши пожелания и собрал все посты по теме многозадачности и производительности в один пост. Приятного чтения, дальше больше, это еще далеко не конец😊

🔹Многозадачность на уровне железа и OS / Kernel Space

1. Многозадачность в OS. Введение.
2. Процессор и его роль в многозадачности
2.1. Про Hyper Threading
3. Процессы. Начало
4. Процессы в Linux
5. Потоки. Начало
6. Потоки в Linux
7. Модели ввода-вывода. Универсальная(блокирующая) модель ввода-вывода
8. Multiplexed IO
9. Asynchronous IO

🔹Легковесные потоки в User Space / Многозадачность в языках программирования

10. Fibers. Виды многозадачности с примерами в языках программирования.
11. Сравнительный обзор двух видов многозадачности
2024/11/29 07:01:00
Back to Top
HTML Embed Code: