Евгений Козлов пишет про IT

@careerunderhood


Руководитель платформенной команды в Т-Банке.

📌 Golang, Python, Ruby
📌 Backend, Databases, System Design
📌 Linux, DevOps, SRE
📌 Computer Science
📌 Личный опыт, Карьерные истории

Для связи: @ea_kozlov

Евгений Козлов пишет про IT

16 Oct, 06:32


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

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

https://sumofbytes.com/blog/understanding-asynchronous-in-linux-io-uring

Евгений Козлов пишет про IT

10 Oct, 10:30


Для всех кто не смог побывать на стриме - опубликована запись с разбором ClickHouse в рамках Книжного клуба.

Приятного просмотра!

https://youtu.be/hQhatSmzQ_8

Евгений Козлов пишет про IT

10 Oct, 06:35


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

Евгений Козлов пишет про IT

09 Oct, 12:04


Всем привет! Я наконец вернулся из отпуска и у меня для вас приготовлена важная новость😇

-----

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

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

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

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

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

Делитесь приглашением с коллегами!

Евгений Козлов пишет про IT

16 Sep, 16:56


Через 5 минут начинаем! 😇

Евгений Козлов пишет про IT

16 Sep, 05:10


Всем привет!

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

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

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

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

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

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

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

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

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

Евгений Козлов пишет про IT

13 Sep, 07:02


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.

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

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

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

Евгений Козлов пишет про IT

12 Sep, 06:39


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

09 Sep, 09:57


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

Дело в том что производительность это довольно сложно и складывается она из большого количества факторов. И для того чтобы грамотно бенчмаркать и профилировать очень полезно знать из чего же сложилась итоговая цифра.

К тому же, я посчитал важным показать внутренности на уровне ОС для того чтобы мы перейдя на уровень ЯП могли посмотреть во что именно превращаются красивые инструкции на языке Go / Python / Java или например Elixir / Rust. А учитывая то что мы рассмотрели на примере Linux - всё есть процессы. Нет никакой секретной магии в языках программирования делающей что-то быстрее или лучше. Всё везде одинаково так как языки для взаимодействия с ОС используют одни и теже примитивы - системные вызовы 🙂

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

Всем спасибо что читали, до встречи в новых постах!

Евгений Козлов пишет про IT

09 Sep, 06:19


А вот это дерево процессов программы в которой был создан дополнительный поток через pthread_create.

htop говорит нам:
- что у процессов общие родители и группа процессов.
- у процессов общий TGID (!)
- у процессов разные PID

Наглядно видим что всё сходится и не противоречит тому что сказано выше😊

Евгений Козлов пишет про IT

09 Sep, 06:19


Добавлю пару примеров для наглядности. На скриншоте дерево процессов сгенерированное через fork(2).

htop говорит нам что:
- Процессы относятся к одной группе процессов (столбец PGRP).
- У каждого процесса свой TGID.
- У процессов разные родители.
- У процессов разные идентификаторы PID.
- PID == TGID у всех процессов.

Исходя из постулатов из поста выше мы можем быть уверены, что перед нами настоящая многопроцессная программа.😊

Евгений Козлов пишет про IT

09 Sep, 05:52


CPU, Memory Models, Concurrency, Multiprocess, Multithreading и Async. Часть 6. Потоки в Linux.

Продолжаем разговор о конкурентности. В прошлом посте были базовые определения связанные с потоками. Сегодня погружаемся во внутренности на примере ядра Linux.

Несколько предварительных фактов о ядре Linux:
Ядро Linux оперирует задачами (tasks). Задача - "что-то", что можно запустить и заменеджить. До появления многопоточности процесс и задача в Linux были синонимами.
- Каждый процесс в системе имеет два идентификатора - Process ID ( PID ) и Thread Group ID ( TGID )
- В "обычном" последовательном процессе PID == TGID.
- В "многопоточном" процессе TGID общий на группу процессов.
- Процессы с общим TGID имеют общие ресурсы.
- Если «многопоточный» процесс имеет PID == TGID, то мы имеем дело с «основным потоком» процесса

Если погружаться в системные вызовы то:
- Вызов getpid() из любого процесса вернет его TGID (= PID "главного потока")
- Вызов gettid() из любого процесса вернет его PID
- Любой тип процесса (раздельные ресурсы, общие) может быть создан с помощью clone(2) системного вызова
- Что будет совместно использоваться процессами, решается путем передачи определенных флагов в вызов функции clone(2)

Директория `/proc`:
- имена папок в директории /proc - это Thread Group ID идентификаторы.
- имена папок в директории /proc/{SOME_TGID}/task - это PIDы потоков.

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

Примечание: getpid() возвращает `TGID` вместо `PID` не просто так. Это сделано для совместимости c POSIX стандартом (потоки должны шарить между собой общий `PID`).

Евгений Козлов пишет про IT

06 Sep, 07:36


CPU, Memory Models, Concurrency, Multiprocess, Multithreading и Async. Часть 5. Потоки. Начало.

Продолжаем разговор о конкурентности. Сегодня разбираемся с потоками. Начнем с основ и базовых определений.

Пото́к выполне́ния (тред; от англ. thread — нить) — наименьшая единица обработки, исполнение которой может быть назначено операционной системой одному ядру процессора. По умолчанию все последовательные программы которые мы пишем имеют один поток исполнения (main).

Зачем нужны потоки если есть процессы, в чем бенефиты?

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

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

Но какова цена? В случае многопоточной программы все потоки имеют доступ к общей памяти и ресурсам. И на плечи программиста ложится обязанность описать алгоритм корректным образом чтобы избежать неконсистентного состояния, состояния гонки, дедлоков итп.

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

Как работать с потоками?

Для создания потоков в большинстве ОС используюется библиотека pthread.h - POSIX THREADS. Поверх нее реализуются уже в конкретных языках программирования красивые классы и абстракции для создания потоков.

На этом на сегодня всё, в следующем посте сделаем Deep Dive в ядро Linux и разберемся как в него завезли потоки если изначально были только процессы.
-----

Напоминаю, что моя личка открыта для всех, а если в личку неудобно то можно написать через Google Forms.

Предлагайте темы для будущих постов, задавайте вопросы или оставляйте отзывы/пожелания.😊

Евгений Козлов пишет про IT

05 Sep, 09:31


Раз уж пост был про процессы и Linux то в качестве практики для всех интересующихся добавлю хороший пример - мультипроцессный веб-сервер. Можно рассмотреть его как Apache Web Server на минималках-минималках😃 (он тоже основан на мультипроцессности).

https://gist.github.com/retr00exe/3196b0a0de61ba6d46f09b1e49561a52

Евгений Козлов пишет про IT

05 Sep, 05:45


CPU, Memory Models, Concurrency, Multiprocess, Multithreading и Async. Часть 4. Процессы в Linux

После небольшой паузы продолжаю цикл постов. Сегодня поговорим о том как в Linux и Unix обстоят дела с процессами. Основные определения разобраны в прошлом посте, здесь сфокусируемся на специфике в Linux и Unix.

Факт №1. Каждый процесс в UNIX и Linux имеет Process ID или PID - идентификатор процесса в ОС. ОС гарантирует что только один процесс в момент времени закреплен за конкретным идентификатором. PID имеет тип данных Int а диапазон допустимых знаений - [0, /proc/sys/kernel/pid_max].

Факт №2. Иерархия процессов в Unix / Linux - древовидная. Есть процессы-родители а есть процессы-потомки, при этом процессы-потомки также могут быть родителями, то есть дерево может иметь большое количество уровней.

Факт №3. Для коммуникации между ОС и процессами а также для процессов между собой существует механизм сигналов - однонаправленная асинхронная комуникация.

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

Факт №5. Если родительский процесс не обрабатывает сигналы посылаемые ему от дочерних процессов то может наступить момент когда все процессы потомки превратились в "зомби процессы".

Факт №6. Для взаимодействия с ресурсами процессы всегда используют системные вызовы.


В Linux Kernel API доступно несколько syscall для работы с процессами:

- семейство вызовов exec для замещения одного процесса другим (process ID остается неизменным, но при этом все ресурсы текущего процесса заменяются новосозданным)
- системный вызов clone для создания клона текущего процесса. Самый важный системный вызов в контексте многопроцессности.
- fork и vfork для создания дочернего процесса. Используют под капотом тот самый системный вызов clone.

Bonus: библиотека POSIX для создания потоков под капотом использует ту же функцию clone и что и функция fork. Получается что потоки в Linux это процессы? Или все таки нет? Ответ на этот вопрос будет в следующем посте завтра 😊

-----

Напоминаю, что моя личка открыта для всех, а если в личку неудобно то можно написать через Google Forms.

Предлагайте темы для будущих постов, задавайте вопросы или оставляйте отзывы/пожелания.😊

Евгений Козлов пишет про IT

29 Aug, 05:32


CPU, Memory Models, Concurrency, Multiprocess, Multithreading и Async. Часть 3. Процессы. Начало

Сегодня поговорим о процессах операционной системы в контексте многозадачности.

Процесс - это экземпляр компьютерной программы. Если компьютерная программа представляет собой некий исполняемый файл на диске с инструкциями для процессора то процесс это экземпляр программы (а следовательно всех ее инструкций) в памяти. Из памяти инструкции передаются CPU.

Что из себя представляет процесс?
- Область виртуальной памяти с инструкциями для CPU.
- Ресурсы операционной системы связанные с данным процессом. (В основном речь про файлы / файловые дескрипторы)
- Текущее состояние программы с точки зрения CPU и его регистров (так как процессы могут не просто ставиться на паузу планировщиком OS а вытесняться из памяти на диск (swap / файл подкачки) нам требуется хранить state чтобы потом было к чему возвращаться.

В каких состояниях процесс может находиться?
- Создан
- Ожидание (ждет когда планировщик выделит процессорное время). В этом состоянии процесс может быть вытеснен в SWAP.
- Исполнение (связанный с процессом CPU молотит инструкции)
- Заблокирован (происходит когда нам попалась инструкция которой нужно взаимодействовать с чем то внешним например файл на локальном диске / ожидание ввода вывода / межпроцессное взаимодействие). В этом состоянии процесс может быть вытеснен в SWAP.
- Завершен. Процесс может завершиться сам с неким кодом ответа, либо его завершит ОС принудительно. Ресурсы связанные с процессором будут удалены.

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

P.S. А что такое виртуальная память? Почему бы не обращаться напрямую?
Техника виртуальной памяти позволяет:
- Добавить дополнительный способ хранения состояний процессов (например упомянутый ранее SWAP, который по сути диск). За счет этого можем позволить ОС работать с большим количеством процессов. И выделять прцоессам больше памяти чем доступно в RAM.
- изолировать процессы друг от друга за счет выдачи непересекающихся виртуальных областей памяти.
- скрыть от разработчика ньюансы работы с памятью, например механики перемещения из RAM на диск и обратно. Процесс не знает вообще ничего о том какая модель памяти на компьютере.

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

На этом на сегодня всё, увидимся завтра, продолжим погружаться в процессы, уже c точки зрения кода😊

-----

Напоминаю, что моя личка открыта для всех, а если в личку неудобно то можно написать через Google Forms.

Предлагайте темы для будущих постов, задавайте вопросы или оставляйте отзывы/пожелания.😊

Евгений Козлов пишет про IT

28 Aug, 05:59


CPU, Memory Models, Concurrency, Multiprocess, Multithreading и Async. Часть 2 - Bonus

Перед тем как переходить от железяк к программированию для полноты картины стоит упомянуть технологию Hyper Threading от Intel с ее логическими ядрами.

В чем ее суть?

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

Переключение между потоками ядер может происходить например из-за:
- промаха в кеше
- ожидании ввода-вывода

С точки зрения науки HyperThreading технология реализует подход SMT - Одновременная многопоточность.

В чем польза от подобного трюка?

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

Например при рендеринге видео, работе с кодом, в общем в сценариях когда мы плодим большое количество задач технология показывает ускорение в пределах 20%. Для игр выигрыша в перформансе может и не быть, зависит от того как запрограммировали игру создатели, как она работает с ресурсами. Нужно проводить тесты.

На этом на сегодня точно всё, увидимся завтра😊

Доп ссылки:
- How much does processor hyper-threading affect the speed of the computer (including games, graphics and simple use)?
- No hyperthreading VS hyperthreading. Testing with V-Ray Next
- When do I need a CPU with hyperthreading?

-----

Напоминаю, что моя личка открыта для всех, а если в личку неудобно то можно написать через Google Forms.

Предлагайте темы для будущих постов, задавайте вопросы или оставляйте отзывы/пожелания.😊

Евгений Козлов пишет про IT

27 Aug, 05:36


CPU, Memory Models, Concurrency, Multiprocess, Multithreading и Async. Часть 2

Продолжаем разбираться с процессорами а именно с тем как развивалась их конфигурация со временем.

В прошлом посте упомянули что у CPU есть важная характеристика - тактовая частота, по сути мощность устройства, чем она выше тем быстрее выполнится наша программа. Казалось бы - давайте повышать тактовую частоту и увеличивать мощности? И это бы работало если бы повышение тактовой частоты не приводило к:
- повышению напряжения
- изменению в дизайне самого устройства
- изменению схемы охлаждения

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

За счет такого маневра мы можем в теории можем исполнять инструкций в секунду в N раз больше чем на аналогичном процессоре с одним ядром. Но это лишь теория, на практике достичь этого невозможно в силу архитектурных ограничений (если получится обсудим это отдельно).

Но есть ли лимиты у подобного способа масштабирования? Теоретически нет. А практически как и всегда нужно помнить о том что чип будет увеличиваться в размерах, нужно его охлаждать и следить за энергопотреблением.

Перейдем к ОС и Софту.
Мультизадачные ОС создавались с учетом новых трендов в железе и могли утилизировать несколько ядер. А как дела обстоят с программами? Если программа была написана в строго последовательном стиле то она не сможет утилизировать появившиеся мощности и скорость работы не увеличится.

Поэтому чтобы делать свои программы быстрыми и производительными нужно учиться писать код по другому. Как? Создавать дополнительные дочерние задачи в рамках основной.
Для этого о мультизадачных ОС появились системные вызовы, например:
- Windows: CreateProcess и CreateThread.
- Linux / Unix: clone(2) как основной системный вызов на основе которого реализована функция fork(2) для создания дочернего процесса.

В отличии от Windows в Linux из коробки не было явного API для создания легковесных потоков (для масштабирования использовались процессы с некоторыми оптимизациями) поэтому в начале 2000х была предпринята попытка сделать "свои потоки" (LinuxThreads) но спустя короткое время она была полностью заменена POSIX Threads. POSIX Threads со временем стал дефакто стандартом для работы с потоками и реализации данного API есть для большинства систем.

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

Спасибо что читали, увидимся завтра😊

Доп ссылки:
- Why haven't CPU clock speeds increased in the last 6 years?
- Why multi core processors?
- Advantages and Disadvantages of Multicore Processors
-----

Напоминаю, что моя личка открыта для всех, а если в личку неудобно то можно написать через Google Forms.

Предлагайте темы для будущих постов, задавайте вопросы или оставляйте отзывы/пожелания.😊