This media is not supported in your browser
VIEW IN TELEGRAM
SIMD операции. Как с их помощью ускорять программы.
Я много писал ранее про производительность и параллелизм, но упустил очень важную функциональность, сильно повлиявшую на все области программирования связанные с вычислениями (графика, сжатие). Как вы уже догадались из названия - речь о SIMD операциях CPU.
Вводная
SIMD расшифровывается как Single Instruction, Multiple Data (Одна инструкция, несколько данных) - архитектура параллельных вычислений, позволяющая одной инструкции обрабатывать несколько элементов данных одновременно. Например, с помощью одной инструкции можно сравнить 16 байт с 16 другими байтами.
В качестве вводного материала рекомендую ознакомиться с Why do we even need SIMD instructions? от Daniel Lemire - признанного эксперта по производительности. Обязательно ознакомьтесь с его блогом и Github, он очень крутой.
Примеры и практические материалы
Чтобы на практике увидеть как работает SIMD и при этом не взорвать мозг рекомендую статью от Andrew Healey о решении задачи подсчета количества слов в строке.
Под катом бенчмарк 5ти вариантов решения:
- Python (byte loop)
- Python + re пакет
- C (scalar loop)
- C + ARM NEON SIMD
- C + ARM NEON SIMD + threads
Что касается real world usage: cамый популярный пример библиотеки с SIMD это simdjson - быстрый парсер JSON на C++, используется в runtime языков программирования и СУБД.
Вместо выводов
Без разницы, работаете ли вы с сложными вычислениями или только погружаетесь в вопросы производительности - SIMD инструкции будут сопровождать вас всю карьеру. Их понимание даже на поверхностном уровне - инвестиция в быстрые и производительные системы под вашим авторством.
Доп ссылки:
- Гайд от CelerData по SIMD. Ребята делают аналитические системы и для них SIMD это мастхэв.
Я много писал ранее про производительность и параллелизм, но упустил очень важную функциональность, сильно повлиявшую на все области программирования связанные с вычислениями (графика, сжатие). Как вы уже догадались из названия - речь о SIMD операциях CPU.
Вводная
SIMD расшифровывается как Single Instruction, Multiple Data (Одна инструкция, несколько данных) - архитектура параллельных вычислений, позволяющая одной инструкции обрабатывать несколько элементов данных одновременно. Например, с помощью одной инструкции можно сравнить 16 байт с 16 другими байтами.
В качестве вводного материала рекомендую ознакомиться с Why do we even need SIMD instructions? от Daniel Lemire - признанного эксперта по производительности. Обязательно ознакомьтесь с его блогом и Github, он очень крутой.
Примеры и практические материалы
Чтобы на практике увидеть как работает SIMD и при этом не взорвать мозг рекомендую статью от Andrew Healey о решении задачи подсчета количества слов в строке.
Под катом бенчмарк 5ти вариантов решения:
- Python (byte loop)
- Python + re пакет
- C (scalar loop)
- C + ARM NEON SIMD
- C + ARM NEON SIMD + threads
Что касается real world usage: cамый популярный пример библиотеки с SIMD это simdjson - быстрый парсер JSON на C++, используется в runtime языков программирования и СУБД.
Вместо выводов
Без разницы, работаете ли вы с сложными вычислениями или только погружаетесь в вопросы производительности - SIMD инструкции будут сопровождать вас всю карьеру. Их понимание даже на поверхностном уровне - инвестиция в быстрые и производительные системы под вашим авторством.
Доп ссылки:
- Гайд от CelerData по SIMD. Ребята делают аналитические системы и для них SIMD это мастхэв.
2👍9🔥6❤2
Продолжаю учиться на курсе Школа технического директора от Стратоплана
Начинаем разбор одного из важнейших аспектов работы CTO - стратегия.
Определения
Технологическая стратегия - план развития инженерной культуры компании в соответствии с общей стратегией компании.
Инженерная культура - комплекс договоренностей и процессов компании по всем технческим стримам, например:
- Delivery
- Project Management
- Reliability
- Quality
- Technology Stack
- Architecture
- Infrastructure
- Security
CTO как технологический бизнес партнер отвечает за внедрение идей бизнеса. При этом если реалии меняются резко, СТО обязан вносить коррективы в технологическую стратегию и как следствие в инженерную культуру.
Выбор технологической стратегии
Перед тем как CTO принимать решение о том в какую сторону идти имеет смысл убедиться что есть четкое понимание бизнеса и внешней среды.
В этом может помочь метод анализа внешней среды PESTEL. Анализировать будем 6 направлений.
- Политические (Political)
- Экономические (Economic)
- Социальные (Social)
- Технологические (Technological)
- Экологические (Environmental)
- Правовые (Legal)
Результат анализа - понять какие факторы имеют на нас наибольшее влияние и корректировать стратегию чтобы максимизировать успех и минимизировать неудачи.
Дальше уже нужно погружаться во внутреннюю кухню. Здесь полезен SWOT анализ. Метод очень популярный, упоминал его ранее, поэтому повторяться не буду.
Имея под рукой 3 вещи:
- Конкретное намерение бизнесаа
- PESTEL артефакт
- SWOT артефакт текущих процессов, команды и культуры.
CTO может принять решение о том как менять тех. стратегию и не наломать дров.
Реализация технологической стратегии
Для приземления всего что мы рассмотрели ранее на реальные и конкретные действия можно использовать методологию GOSPA, она состоит из:
- Goals (цели бизнеса, ожидания от нашего тех. отдела)
- Objectives (цели нашего тех. отдела)
- Strategy (непосредственно то что мы провели ранее, анализ и выбор пути)
- Plans (конкретные изменения, инженерная культура)
- Actions (задачи на реализацию изменеий, контроль выполнения и качества)
Изначально методология предназначалась для маркетинга, при этом тренер продемонстрировал как можно ее уложить на "технорельсы".
Важно: перед тем как все это реализовывать не забываем считать стоимость внедрения той или иной стратегии.
Начинаем разбор одного из важнейших аспектов работы CTO - стратегия.
Определения
Технологическая стратегия - план развития инженерной культуры компании в соответствии с общей стратегией компании.
Инженерная культура - комплекс договоренностей и процессов компании по всем технческим стримам, например:
- Delivery
- Project Management
- Reliability
- Quality
- Technology Stack
- Architecture
- Infrastructure
- Security
CTO как технологический бизнес партнер отвечает за внедрение идей бизнеса. При этом если реалии меняются резко, СТО обязан вносить коррективы в технологическую стратегию и как следствие в инженерную культуру.
Выбор технологической стратегии
Перед тем как CTO принимать решение о том в какую сторону идти имеет смысл убедиться что есть четкое понимание бизнеса и внешней среды.
В этом может помочь метод анализа внешней среды PESTEL. Анализировать будем 6 направлений.
- Политические (Political)
- Экономические (Economic)
- Социальные (Social)
- Технологические (Technological)
- Экологические (Environmental)
- Правовые (Legal)
Результат анализа - понять какие факторы имеют на нас наибольшее влияние и корректировать стратегию чтобы максимизировать успех и минимизировать неудачи.
Дальше уже нужно погружаться во внутреннюю кухню. Здесь полезен SWOT анализ. Метод очень популярный, упоминал его ранее, поэтому повторяться не буду.
Имея под рукой 3 вещи:
- Конкретное намерение бизнесаа
- PESTEL артефакт
- SWOT артефакт текущих процессов, команды и культуры.
CTO может принять решение о том как менять тех. стратегию и не наломать дров.
Реализация технологической стратегии
Для приземления всего что мы рассмотрели ранее на реальные и конкретные действия можно использовать методологию GOSPA, она состоит из:
- Goals (цели бизнеса, ожидания от нашего тех. отдела)
- Objectives (цели нашего тех. отдела)
- Strategy (непосредственно то что мы провели ранее, анализ и выбор пути)
- Plans (конкретные изменения, инженерная культура)
- Actions (задачи на реализацию изменеий, контроль выполнения и качества)
Изначально методология предназначалась для маркетинга, при этом тренер продемонстрировал как можно ее уложить на "технорельсы".
Важно: перед тем как все это реализовывать не забываем считать стоимость внедрения той или иной стратегии.
👍17❤10🔥10💩3👎1
Concurrency, Synchronization and Consistency. Пост №15. Пишем собственный Mutex на Golang. runtime.Gosched, Starvation и бенчмарки
Как и обещал ранее - написал целую статью о своем опыте написания собственного Mutex на Golang.
Внутри:
- Наивная реализация
- Улучшенная (справится лучше sync.Mutex)
- Тест на двух задачах (IO и CPU)
- Выводы
Приятного чтения! Буду безумно рад вашей поддержке в продвижении статьи (конечно, если она вам понравилась). Давайте холиварить и делиться знаниями друг с другом.
P.S. Все прошлые посты серии в закрепленном сообщении.
Как и обещал ранее - написал целую статью о своем опыте написания собственного Mutex на Golang.
Внутри:
- Наивная реализация
- Улучшенная (справится лучше sync.Mutex)
- Тест на двух задачах (IO и CPU)
- Выводы
Приятного чтения! Буду безумно рад вашей поддержке в продвижении статьи (конечно, если она вам понравилась). Давайте холиварить и делиться знаниями друг с другом.
P.S. Все прошлые посты серии в закрепленном сообщении.
Хабр
Concurrency на примерах. Собственная реализация Mutex на Go + сравнение с sync.Mutex. Часть 1
Оформил в виде статьи свой опыт написания с нуля примитивов синхронизации на чистом Go, совместимых c реализациями из стандартной библиотеки. Цель - на понятных примерах посмотреть как работает под...
🔥9❤7👍4
Запись выступления на Yandex Neuro Scale 2025
Ребята из Yandex Cloud опубликовали записи всех выступлений c конференции. Мы с Русланом выступали в секции Cases, так как наш доклад был синергией бизнеса и техники. Отдельным поводом для гордости было услышать от организаторов что на нашем потоке доклад был самым посещаемым😇
Ссылка на запись
Задавайте вопросы по содержанию, если увижу что вам интересно - сделаю подробные посты с разбором технических деталей.
Приятного просмотра!
Ребята из Yandex Cloud опубликовали записи всех выступлений c конференции. Мы с Русланом выступали в секции Cases, так как наш доклад был синергией бизнеса и техники. Отдельным поводом для гордости было услышать от организаторов что на нашем потоке доклад был самым посещаемым😇
Ссылка на запись
Задавайте вопросы по содержанию, если увижу что вам интересно - сделаю подробные посты с разбором технических деталей.
Приятного просмотра!
👍15🔥10❤2
Недавно мой коллега Максим в своем канале сделал большой пост с разбором бинарных деревьев поиска, сложность, примеры задач, всё в этом духе.
Невольно вспомнил как сам несколько лет назад сидел на литкоде и практиковался в написании кода, в том числе обходя деревья.
И решил поделиться собственным "ноу-хау" нигде не подсмотренным. Как используя одну очень популярную Python конструкцию обходить деревья элегантно и красиво, и даже комбинировать обходы вместе.
https://habr.com/ru/articles/957050/
Обычно я ничего не публикую в выходные, но со следующей недели выхожу из отпуска, так что на всякий случай лучше сегодня😁
Приятного чтения! Расскажите в комментах, что думаете насчет такого варианта решения задачи😊
Невольно вспомнил как сам несколько лет назад сидел на литкоде и практиковался в написании кода, в том числе обходя деревья.
И решил поделиться собственным "ноу-хау" нигде не подсмотренным. Как используя одну очень популярную Python конструкцию обходить деревья элегантно и красиво, и даже комбинировать обходы вместе.
https://habr.com/ru/articles/957050/
Обычно я ничего не публикую в выходные, но со следующей недели выхожу из отпуска, так что на всякий случай лучше сегодня😁
Приятного чтения! Расскажите в комментах, что думаете насчет такого варианта решения задачи😊
Хабр
[Алгоритмы, Задачки] Элегантно и идиоматично обходим двоичное дерево поиска на Python 3
Недавно увидел на просторах телеграмма заметку о том как решать алгоритмические задачи на деревья. Вспомнил, что в свое время у меня тоже были некоторые наработки, при этом они отличаются от того что...
2❤13👍7🔥4
Евгений Козлов пишет про IT
Запись выступления на Yandex Neuro Scale 2025 Ребята из Yandex Cloud опубликовали записи всех выступлений c конференции. Мы с Русланом выступали в секции Cases, так как наш доклад был синергией бизнеса и техники. Отдельным поводом для гордости было услышать…
Завершающий пост про осенний доклад на Yandex Neuro Scale 2025
К нам обратились организаторы и предложили поучаствовать в подкасте "404 секунды". Выпуск посвящен технолгиям в Финтехе. В формате блица ответил на основные вопросы о том как же устроена наша платформа, а в конце дал совет тем кто хочет реализовать систему похожую на Statist😊
Youtube
VK Video
К нам обратились организаторы и предложили поучаствовать в подкасте "404 секунды". Выпуск посвящен технолгиям в Финтехе. В формате блица ответил на основные вопросы о том как же устроена наша платформа, а в конце дал совет тем кто хочет реализовать систему похожую на Statist😊
Youtube
VK Video
YouTube
Какие технологии драйвят финансовую отрасль
#Финтех #AI #Биометрия #Аналитика #ЦифроваяТрансформация #Банки #ТехнологииБудущего #ФинСектор #Инновации #YandexCloud #Citi #TБанк #Statist
Погружаемся в тему современных финансовых технологий: какие инновации трансформируют банковскую сферу и финтех-сервисы…
Погружаемся в тему современных финансовых технологий: какие инновации трансформируют банковскую сферу и финтех-сервисы…
🔥5👍2❤1
Заканчиваю учиться на курсе Школа технического директора от Стратоплана
Сегодня будет саммари последнего модуля курса, авторы назвали его "Продукты и жизнь"
🔵 День №1 Мотивация в работе СТО
В этом блоке мы с тренером препаривали как видно из названия мотивацию. С точки зрения тренера
Разбирали эффективные и не очень варианты мотивации (денежки, плюшки, задачи, всё по классике).
Тренер поделился своим видением как СТО нужно работать с мотивацией подчиненных:
- Чёткие и понятные цели + обсуждать их связь с целями сотрудника.
- Знание каждым руководителем мотиваторов своих подчинённых.
- Регулярный мониторинг настроения и вовлечённости
- Личное вовлечение руководителя.
Если формально - руководитель с точки зрения софтов может взаимодействовать с сотрудниками в 4х направлениях:
- Удовлетворённость
- Мотивация
- Вовлечённость
- Демотивация
Далеко не все советы тренера по работе с ними мне зашли. Например я не верю в опросники вовлеченности. Расскажите в комментариях как относитесь к ним вы, мне будет интересно ваше мнение.
Если подытожить одной мыслью блок то каждому руководителю (не обязательно СТО) важно регулярно задавать себе такой вопрос:
Модуль мне понравился, при этом откровений из него я почти не вынес, многие вещи делал по наитию и так, потому что считаю их "здравым смыслом". Получил подтверждение для себя что делаю правильно, плюс улеглось в голове.
🔵 День №2 Управление продуктом
Этот блок я бы назвал "Как стать продактом в плане хардскиллов за 4 часа".
- Поговорили про методологии разработки, почему "Agile победил"
- Виды исследований продуктовых гипотез. Жизненный цикл гипотезы.
- Customer interviews. Как говорить с пользователями.
- Прототип vs MVP. Как сформировать Minimum Viable Product
- Управление продуктом на основе данных. Метрика полярной звезды (North Star Metric). Дерево метрик.
Тренер часто делал отсылки к очень известному дядечке, автору книги The Startup Owner’s manual
В этом модуле я почти ничего нового не узнал, почти все вещи мне были и так понятны, думаю что в теории могу джуном продактом устроиться если разработка доканает😁
🔵 День №3 Рефлексия
Завершающий день мы посвятили осмыслению пройденного пути. Написали "письмо другу" и много разгоняли в групповых комнатах о результатах и приобретенном опыте.
-----
На этом всё (почти), следующий пост будем завершающим, подведу итоги прохождения курса для себя.
Спасибо что читали, до встречи!
Сегодня будет саммари последнего модуля курса, авторы назвали его "Продукты и жизнь"
🔵 День №1 Мотивация в работе СТО
В этом блоке мы с тренером препаривали как видно из названия мотивацию. С точки зрения тренера
Мотивация — это малоконтролируемая реакция психики на внутренние и внешние обстоятельства человека.
Разбирали эффективные и не очень варианты мотивации (денежки, плюшки, задачи, всё по классике).
Тренер поделился своим видением как СТО нужно работать с мотивацией подчиненных:
- Чёткие и понятные цели + обсуждать их связь с целями сотрудника.
- Знание каждым руководителем мотиваторов своих подчинённых.
- Регулярный мониторинг настроения и вовлечённости
- Личное вовлечение руководителя.
Если формально - руководитель с точки зрения софтов может взаимодействовать с сотрудниками в 4х направлениях:
- Удовлетворённость
- Мотивация
- Вовлечённость
- Демотивация
Далеко не все советы тренера по работе с ними мне зашли. Например я не верю в опросники вовлеченности. Расскажите в комментариях как относитесь к ним вы, мне будет интересно ваше мнение.
Если подытожить одной мыслью блок то каждому руководителю (не обязательно СТО) важно регулярно задавать себе такой вопрос:
Каким образом я как руководитель забочусь о том, чтобы мои подчиненные выполняя свою работу наилучшим образом, могли удовлетворять свои потребности наилучшим образом?
Модуль мне понравился, при этом откровений из него я почти не вынес, многие вещи делал по наитию и так, потому что считаю их "здравым смыслом". Получил подтверждение для себя что делаю правильно, плюс улеглось в голове.
🔵 День №2 Управление продуктом
Этот блок я бы назвал "Как стать продактом в плане хардскиллов за 4 часа".
- Поговорили про методологии разработки, почему "Agile победил"
- Виды исследований продуктовых гипотез. Жизненный цикл гипотезы.
- Customer interviews. Как говорить с пользователями.
- Прототип vs MVP. Как сформировать Minimum Viable Product
- Управление продуктом на основе данных. Метрика полярной звезды (North Star Metric). Дерево метрик.
Тренер часто делал отсылки к очень известному дядечке, автору книги The Startup Owner’s manual
В этом модуле я почти ничего нового не узнал, почти все вещи мне были и так понятны, думаю что в теории могу джуном продактом устроиться если разработка доканает😁
🔵 День №3 Рефлексия
Завершающий день мы посвятили осмыслению пройденного пути. Написали "письмо другу" и много разгоняли в групповых комнатах о результатах и приобретенном опыте.
-----
На этом всё (почти), следующий пост будем завершающим, подведу итоги прохождения курса для себя.
Спасибо что читали, до встречи!
Школа менеджмента STRATOPLAN
Курс «СТО» - Школа менеджмента STRATOPLAN
Практический тренинг-симулятор для текущих и будущих технических директоров
👍13🔥9❤8👎1
Подвожу итоги обучения на курсе Школа технического директора от Стратоплана
Вот и закончилась "авантюра". Пришло время порефлексировать о том как прошли 9 месяцев обучения в Стратоплане.
Напомню, обучение состояло из 9 модулей (по ссылкам посты):
🟠 Роль и проблематизация
🟠Миддл-менеджмент
🔸Делегирование
🟠Работа с процессами
🔸Процессы в продукте и проекте
🔸Строгие и гибкие процессы
🟠 Stakeholders management
🟠 Инфраструктура
🔸Закупки
🟠 Работа с операционкой СТО
🔸Бюджет. Бюджетирование для СТО
🔸Метрики
🟠 Стратегия
🟠 Управление ресурсами
🟠 Продукты и жизнь
Каждый модуль - 3хдневка с пятницы по воскресеньнье. С 10 до 15 по МСК.
🔵Вывод #1 - на учебу нужны силы
Когда все только начиналось мне казалось что всё будет "изи" Как же я ошибался. Вы должны быть готовы отпрашиваться на работе по пятницам, и посвящать учебе законные выходные. Мне было особенно сложно не заглядывать в рабочий мессенджер во время учебы и пропускать встречи.
🔵 Вывод #2 - курс это инвестиция которая непонятно когда окупится (лично для меня)
Я сейчас работаю руководителем команды разработки. Нахрена я пошел учиться на СТО спросите вы? Почему бы не пойти учиься на тимлида тимлидов для начала, если уж сильно надо?
Мне очень нравится моя роль и продукт в котором работаю. Приходится решать инженерные челленджи, а не бизнесовые / энтерпрайзные. Уход в чистый менеджмент я не рассматриваю, именно поэтому я решил что если уж и делать дальнейшие шаги в карьере то идеальным для меня было бы попробовать себя в роли CTO в своем/чужом стартапе. Звучит конечно амбициозно и красиво, но для меня это не про лычку а про роль. Я не хочу забывать технику. И хочу быть ближе к принятию решений, также как и сейчас.
🔵 Вывод #3 - геймификация и работа в группах это клево
Весь курс нас сопровождал тренер, в его обязанности входило ведение игровой вселенной. Студенты разделены на команды, каждая команда зарабатывает очки лояльности остального C-level, выдаются опционы, попадаем в разные приятные и не очень ситуации. Всё как в жизни.
Я посмотрел на тренера со стороны и понял, как же все таки важно вовлекать. По мне это и отличает хорошую учебную программу от посредственной.
Хочу научиться этому искусству для работы и личных нужд, например оформлять посты так, чтобы вы чаще писали комментарии и вовлекались в обсуждение😇
🔵 Отзыв о содержимом курса
Для меня каждый модуль был полезен. Не было такого что я слушаю и понимаю что мне втирают дичь. Особенно хороши были модули про стратегию, стейкхолдеров, инфру и ресурсы. Их я буду точно пересматривать, благо доступ к материалам пожизненный.
Учебой доволен, надеюсь что у меня будет возможность поработать в этой роли в обозримом будущем. Спасибо что читали, надеюсь вам были интереcны посты этой серии😊
Вот и закончилась "авантюра". Пришло время порефлексировать о том как прошли 9 месяцев обучения в Стратоплане.
Напомню, обучение состояло из 9 модулей (по ссылкам посты):
🟠 Роль и проблематизация
🟠Миддл-менеджмент
🔸Делегирование
🟠Работа с процессами
🔸Процессы в продукте и проекте
🔸Строгие и гибкие процессы
🟠 Stakeholders management
🟠 Инфраструктура
🔸Закупки
🟠 Работа с операционкой СТО
🔸Бюджет. Бюджетирование для СТО
🔸Метрики
🟠 Стратегия
🟠 Управление ресурсами
🟠 Продукты и жизнь
Каждый модуль - 3хдневка с пятницы по воскресеньнье. С 10 до 15 по МСК.
🔵Вывод #1 - на учебу нужны силы
Когда все только начиналось мне казалось что всё будет "изи" Как же я ошибался. Вы должны быть готовы отпрашиваться на работе по пятницам, и посвящать учебе законные выходные. Мне было особенно сложно не заглядывать в рабочий мессенджер во время учебы и пропускать встречи.
🔵 Вывод #2 - курс это инвестиция которая непонятно когда окупится (лично для меня)
Я сейчас работаю руководителем команды разработки. Нахрена я пошел учиться на СТО спросите вы? Почему бы не пойти учиься на тимлида тимлидов для начала, если уж сильно надо?
Мне очень нравится моя роль и продукт в котором работаю. Приходится решать инженерные челленджи, а не бизнесовые / энтерпрайзные. Уход в чистый менеджмент я не рассматриваю, именно поэтому я решил что если уж и делать дальнейшие шаги в карьере то идеальным для меня было бы попробовать себя в роли CTO в своем/чужом стартапе. Звучит конечно амбициозно и красиво, но для меня это не про лычку а про роль. Я не хочу забывать технику. И хочу быть ближе к принятию решений, также как и сейчас.
🔵 Вывод #3 - геймификация и работа в группах это клево
Весь курс нас сопровождал тренер, в его обязанности входило ведение игровой вселенной. Студенты разделены на команды, каждая команда зарабатывает очки лояльности остального C-level, выдаются опционы, попадаем в разные приятные и не очень ситуации. Всё как в жизни.
Я посмотрел на тренера со стороны и понял, как же все таки важно вовлекать. По мне это и отличает хорошую учебную программу от посредственной.
Хочу научиться этому искусству для работы и личных нужд, например оформлять посты так, чтобы вы чаще писали комментарии и вовлекались в обсуждение😇
🔵 Отзыв о содержимом курса
Для меня каждый модуль был полезен. Не было такого что я слушаю и понимаю что мне втирают дичь. Особенно хороши были модули про стратегию, стейкхолдеров, инфру и ресурсы. Их я буду точно пересматривать, благо доступ к материалам пожизненный.
Учебой доволен, надеюсь что у меня будет возможность поработать в этой роли в обозримом будущем. Спасибо что читали, надеюсь вам были интереcны посты этой серии😊
👏30❤12👍6🔥2
Евгений Козлов пишет про IT
Concurrency, Synchronization and Consistency. Пост №15. Пишем собственный Mutex на Golang. runtime.Gosched, Starvation и бенчмарки Как и обещал ранее - написал целую статью о своем опыте написания собственного Mutex на Golang. Внутри: - Наивная реализация…
Concurrency, Synchronization and Consistency. Пост №16. Политики планирования и приоритеты потоков в вытесняющей многозадачности.
Вместо предисловия - рекомендуемую освежить в памяти посты:
- Fibers и виды многозадачности
- Сравнение видов многозадачности
Этот пост является продолжением этих постов. Из них узнаете в подробностях про вытесняющую многозадачность, которую будем разбирать сегодня.
_
Мы довольно много времени посвятили разбору примитивов синхронизации, их внутренностям и разбором разных "почему" и "зачем".
Сегодня хочется выйти из пузыря самих примитивов и посмотреть на среду в которой работают сами потоки и то как влияет синхронизация на эффективность программы.
🔵 Нечестная (unfair) блокировка
В прошлых постах я рассказывал про fast path - трюк в коде mutex благодаря которому улучшается производительность. Его суть - перед тем как засыпать поток какое то время пытается захватить мьютекс в надежде что тот освободится "вот вот". А если не получилось захватить, идем в спячку и ждем пока нас разбудят.
Видите ли вы в таком подходе намек на нечестную игру? Я да. Вместо того чтобы распределять процессорное время по принципу FIFO (очереди) у нас могут возникать ситуация, когда ресурс захватывает тот кто пришел последним и "угоняет" мьютекс пока те кто пришли раньше спят.
С точки зрения разработки прикладных программ может показаться что проблема надуманная, ведь благодаря этой оптимизации программы работают быстрее (меньше циклов засыпания, пробуждения и нагрузки на ОС). Ну и что что нет честности, зато быстро. И будете правы, но для полноты картины нам стоит рассмотреть другую сторону медали.
Примеры задач где нужна честная (fair) блокировка и строгие приоритеты.
Мир программирования не ограничивается прикладным софтом, помимо этого существуют:
- Станки с ЧПУ.
- Роботы.
- Задачи в реальном времени. Например работа с аудио и видео.
- Телеком.
- Системы критичные к latency (например трейдинг).
- Авиация, космос.
И вот для них описанный выше подход может быть неприемлем. Потому что при нечестной блокировке нет ни намека на предсказуемый отклик от нашей системы. Какой то поток занимающийся низкоприоритетной задачей может начать отъедать ресурс и привести к деградации системы.
🔵 Честная блокировка. Как к ней подобраться?
Несмотря на то что по умолчанию все внутренности ОС заточены на то чтобы всё работало быстро - у разработчиков есть возможность влиять на планирование потоков операционной системой и приоритеты.
🔵 Как изменить приоритет потока?
Делается это через вызов nice(2). Планировщик (CFS - Completely Fair Scheduler) использует установленные потокам значения при расчёте веса задачи (load_weight), определяя, сколько CPU времени ей выделять. Функция nice(2) применяется только для политики планирования по умолчанию (SCHED_OTHER в Linux). Принимает целочисленные значения в диапазоне [-20, +19]. Чем ниже значение тем больше веса у потока.
🔵 Политики планирования потоков
Помимо приоритетов у нас также есть возможность управлять планированием потоков через политики:
-
-
- SCHED_FIFO: политика «первым пришел/первым ушел» в режиме реального времени (если в программе есть потоки с такой политикой в состоянии runnable - все остальные потоки с более слабой политикой будут немедленно остановлены / вытеснены).
-
Вот такой обзор получился, надеюсь мне удалось не слишком замудрено объяснить основную базу, чтобы вы могли в случае чего углубиться в вопрос самостоятельно. Буду рад если поделитесь обратной связью в комментариях, расскажите о своем опыте, вдруг вам будет чем дополнить материал.
Полезные ссылки:
- sched(7) — Linux manual page
Вместо предисловия - рекомендуемую освежить в памяти посты:
- Fibers и виды многозадачности
- Сравнение видов многозадачности
Этот пост является продолжением этих постов. Из них узнаете в подробностях про вытесняющую многозадачность, которую будем разбирать сегодня.
_
Мы довольно много времени посвятили разбору примитивов синхронизации, их внутренностям и разбором разных "почему" и "зачем".
Сегодня хочется выйти из пузыря самих примитивов и посмотреть на среду в которой работают сами потоки и то как влияет синхронизация на эффективность программы.
🔵 Нечестная (unfair) блокировка
В прошлых постах я рассказывал про fast path - трюк в коде mutex благодаря которому улучшается производительность. Его суть - перед тем как засыпать поток какое то время пытается захватить мьютекс в надежде что тот освободится "вот вот". А если не получилось захватить, идем в спячку и ждем пока нас разбудят.
Видите ли вы в таком подходе намек на нечестную игру? Я да. Вместо того чтобы распределять процессорное время по принципу FIFO (очереди) у нас могут возникать ситуация, когда ресурс захватывает тот кто пришел последним и "угоняет" мьютекс пока те кто пришли раньше спят.
С точки зрения разработки прикладных программ может показаться что проблема надуманная, ведь благодаря этой оптимизации программы работают быстрее (меньше циклов засыпания, пробуждения и нагрузки на ОС). Ну и что что нет честности, зато быстро. И будете правы, но для полноты картины нам стоит рассмотреть другую сторону медали.
Примеры задач где нужна честная (fair) блокировка и строгие приоритеты.
Мир программирования не ограничивается прикладным софтом, помимо этого существуют:
- Станки с ЧПУ.
- Роботы.
- Задачи в реальном времени. Например работа с аудио и видео.
- Телеком.
- Системы критичные к latency (например трейдинг).
- Авиация, космос.
И вот для них описанный выше подход может быть неприемлем. Потому что при нечестной блокировке нет ни намека на предсказуемый отклик от нашей системы. Какой то поток занимающийся низкоприоритетной задачей может начать отъедать ресурс и привести к деградации системы.
🔵 Честная блокировка. Как к ней подобраться?
Несмотря на то что по умолчанию все внутренности ОС заточены на то чтобы всё работало быстро - у разработчиков есть возможность влиять на планирование потоков операционной системой и приоритеты.
🔵 Как изменить приоритет потока?
Делается это через вызов nice(2). Планировщик (CFS - Completely Fair Scheduler) использует установленные потокам значения при расчёте веса задачи (load_weight), определяя, сколько CPU времени ей выделять. Функция nice(2) применяется только для политики планирования по умолчанию (SCHED_OTHER в Linux). Принимает целочисленные значения в диапазоне [-20, +19]. Чем ниже значение тем больше веса у потока.
🔵 Политики планирования потоков
Помимо приоритетов у нас также есть возможность управлять планированием потоков через политики:
-
SCHED_OTHER или SCHED_NORMAL: Политика по умолчанию-
SCHED_IDLE: Более низкий приоритет, чем SCHED_OTHER- SCHED_FIFO: политика «первым пришел/первым ушел» в режиме реального времени (если в программе есть потоки с такой политикой в состоянии runnable - все остальные потоки с более слабой политикой будут немедленно остановлены / вытеснены).
-
SCHED_RR: Политика кругового перебора в реальном времени. Улучшение политики FIFO.Вот такой обзор получился, надеюсь мне удалось не слишком замудрено объяснить основную базу, чтобы вы могли в случае чего углубиться в вопрос самостоятельно. Буду рад если поделитесь обратной связью в комментариях, расскажите о своем опыте, вдруг вам будет чем дополнить материал.
Полезные ссылки:
- sched(7) — Linux manual page
❤9👍6🔥5
Concurrency, Synchronization and Consistency. Пост №17. Введение в взаимную блокировку (deadlock). Решаем задачу об обедающих философах на Go.
Пора перейти от уровня ОС и низкоуровневых примитивов поближе к реальным задачам и челленджам. Сегодня нестареющая классика - задача о философах.
Задача: разработать алгоритм, при котором ни один из философов не будет голодать, то есть будет вечно чередовать приём пищи и размышления.
🔵 Наивная реализация
Предлагаю сначала ничего не выдумывать, а просто закодировать то что первым приходит в голову.
Go Playground
Не самый сложный код. Но есть загвоздка - он неправильный. Вилок столько же сколько и философов и когда каждый хватает левую то на столе не остается вилок. Программа зависла навсегда. Конец. У этой проблемы даже есть название - взаимная блокировка.
Попробуем не вникая в глубокие теоретические рассуждения и алгоритмы решить задачу так как бы мы ее решали в реальной жизни.
🔵 Просим философов быть менее предсказуемыми
Чтобы устранить голодовую смерть можно изменить порядок взятия вилок со стола. Но не для всех философов одновременно, а например для одного. Этого будет достаточно.
Полный код: Go Playground
🔵Добавляем официанта
Вместо того чтобы вносить хак в программу и делать поведение участников непохожим на остальных можно добавить помощника - официанта.
Это классический пример синхронизации через семафор. Каждый философ перед тем как хвататься за вилки просит разрешения у официанта. Если он принял запрос, значит доступ к вилкам есть у 3х или менее философов, а значит мы не попадем во взаимную блокировку.
Полный код: Go Playground
🔵 Итоги
В несколько этапов мы прошли путь от решения в лоб к двум корректным:
- с хаком в коде (у него есть название, кто знает пишите в комментарии)
- c семафором.
При этом не был рассмотрен вопрос - как реализовать справедливое решение, чтобы все кушали одинаково? Ставьте 🔥, если увижу ваш интерес - обязательно рассмотрим и этот вопрос.
На этом всё, буду рад вашей ОС и реакциям, особенно интересно - был ли пост понятен без предварительной теории. Её могло быть много, но я осознанно сфокусировался на практике😇
📖 Оглавление
Пора перейти от уровня ОС и низкоуровневых примитивов поближе к реальным задачам и челленджам. Сегодня нестареющая классика - задача о философах.
Пять философов сидят вокруг круглого стола, перед каждым стоит тарелка спагетти. На столе между каждой парой ближайших философов лежит по одной вилке.
Каждый философ может либо есть, либо размышлять. Приём пищи не ограничен количеством оставшихся спагетти — подразумевается бесконечный запас. Тем не менее, философ может есть только тогда, когда держит две вилки — взятую справа и слева.
Каждый философ может взять ближайшую вилку (если она доступна) или положить — если он уже держит её. Взятие каждой вилки и возвращение её на стол являются раздельными действиями, которые должны выполняться одно за другим.
Задача: разработать алгоритм, при котором ни один из философов не будет голодать, то есть будет вечно чередовать приём пищи и размышления.
🔵 Наивная реализация
Предлагаю сначала ничего не выдумывать, а просто закодировать то что первым приходит в голову.
Go Playground
package main
import (
"fmt"
"sync"
)
func main() {
const philosophers = 5
forks := make([]sync.Mutex, philosophers)
for p := range philosophers {
go philosopher(p, &forks[p], &forks[(p+1)%philosophers])
}
quit := make(chan struct{})
<-quit
}
func philosopher(p int, left *sync.Mutex, right *sync.Mutex) {
for {
left.Lock()
right.Lock()
fmt.Println("Philosopher", p, "Eating...")
left.Unlock()
right.Unlock()
fmt.Println("Philosopher", p, "ate :)")
}
}
Не самый сложный код. Но есть загвоздка - он неправильный. Вилок столько же сколько и философов и когда каждый хватает левую то на столе не остается вилок. Программа зависла навсегда. Конец. У этой проблемы даже есть название - взаимная блокировка.
Попробуем не вникая в глубокие теоретические рассуждения и алгоритмы решить задачу так как бы мы ее решали в реальной жизни.
🔵 Просим философов быть менее предсказуемыми
Чтобы устранить голодовую смерть можно изменить порядок взятия вилок со стола. Но не для всех философов одновременно, а например для одного. Этого будет достаточно.
Полный код: Go Playground
func philosopher(p int, left *sync.Mutex, right *sync.Mutex) {
for {
if p == 4 {
right.Lock()
left.Lock()
} else {
left.Lock()
right.Lock()
}
fmt.Println("Philosopher", p, "Eating...")
time.Sleep(time.Millisecond)
left.Unlock()
right.Unlock()
fmt.Println("Philosopher", p, "ate :)")
}
}🔵Добавляем официанта
Вместо того чтобы вносить хак в программу и делать поведение участников непохожим на остальных можно добавить помощника - официанта.
Это классический пример синхронизации через семафор. Каждый философ перед тем как хвататься за вилки просит разрешения у официанта. Если он принял запрос, значит доступ к вилкам есть у 3х или менее философов, а значит мы не попадем во взаимную блокировку.
Полный код: Go Playground
steward := make(chan int, philosophers-1)
...
func philosopher(p int, left *sync.Mutex, right *sync.Mutex, steward chan int) {
for {
steward <- p
left.Lock()
right.Lock()
fmt.Println("Philosopher", p, "Eating...")
time.Sleep(time.Millisecond)
left.Unlock()
right.Unlock()
<-steward
fmt.Println("Philosopher", p, "ate :)")
}
}
🔵 Итоги
В несколько этапов мы прошли путь от решения в лоб к двум корректным:
- с хаком в коде (у него есть название, кто знает пишите в комментарии)
- c семафором.
При этом не был рассмотрен вопрос - как реализовать справедливое решение, чтобы все кушали одинаково? Ставьте 🔥, если увижу ваш интерес - обязательно рассмотрим и этот вопрос.
На этом всё, буду рад вашей ОС и реакциям, особенно интересно - был ли пост понятен без предварительной теории. Её могло быть много, но я осознанно сфокусировался на практике😇
📖 Оглавление
1🔥36👍4❤2
Всем привет! Ко мне обратились ребята из Стратоплана за помощью в распространении совместного исследования с Devcrowd. Наверняка вы ранее вам попадались их исследования по другим стримам, например по Go или Teamlead.
В этот раз тема исследования - Стратегия.
Мне тема близка, на работе приходится в эту сторону копать, участвую в обсуждении стратегических вопросов отдела в котором работаю.
Если вам есть чем поделиться переходите по ссылочке.
https://stratoplan-school.com/research/
В этот раз тема исследования - Стратегия.
Мы хотим понять, насколько стратегия действительно применяется на практике: кто её формулирует, как она связана с бизнес-целями, влияет ли на повседневную работу команд, и с какими барьерами сталкиваются те, кто участвует в стратегических процессах.
Мне тема близка, на работе приходится в эту сторону копать, участвую в обсуждении стратегических вопросов отдела в котором работаю.
Если вам есть чем поделиться переходите по ссылочке.
https://stratoplan-school.com/research/
Школа менеджмента STRATOPLAN
Исследование стратегии
Стратоплан объединился с DevCrowd, чтобы провести большое исследование о том, как управленцы в ИТ-компаниях — от тимлидов до C-level — работают со стратегией
❤12👍10🔥10
Concurrency, Synchronization and Consistency. Пост №18. Golang sync.Mutex internals
В прошлом посте мы решали практическую задачку и использовали язык Go. Его основная особенность - наличие собственного рантайма в рамках которого происходит управление и планирование горутин - потоков исполнения не связанных явно 1к1 с потоками ОС.
А если у языка собственный рантайм и "собственные" потоки, то и должны быть "собственные" примитивы синхронизации, умеющие работать с горутинами. И они действительно есть. Основным примитивом синхронизации в Go является
У ребят из VictoriaMetrics получился великолепный разбор внутреннего устройства
Поэтому советую освежить в памяти предыдущие посты цикла. Увидите, что многие идеи и оптимизации придуманные для ОС были использованы в рантайме Go. Уровень абстракции выше, а проблемы и вызовы такие же😊
Внутри вас ждёт:
- Внутреннее представление
- Как выглядят
- Как
- Как происходит
https://victoriametrics.com/blog/go-sync-mutex/
-----
Если вам понравился пост, поддержите его реакциями и комментариями. Мне важно увидеть вашу обратную связь. Спасибо!
P.S. Следующая публикация вероятнее всего будет на Хабре, выпуск намечен на следующую неделю. Будем как в прошлый раз писать свои велосипеды в погоне за эталонными реализациями, оценивать результаты бенчмарками и профилями😊
В прошлом посте мы решали практическую задачку и использовали язык Go. Его основная особенность - наличие собственного рантайма в рамках которого происходит управление и планирование горутин - потоков исполнения не связанных явно 1к1 с потоками ОС.
А если у языка собственный рантайм и "собственные" потоки, то и должны быть "собственные" примитивы синхронизации, умеющие работать с горутинами. И они действительно есть. Основным примитивом синхронизации в Go является
sync.Mutex.У ребят из VictoriaMetrics получился великолепный разбор внутреннего устройства
sync.Mutex c красивыми иллюстрациями и кодом. Я бы не смог рассказать лучше, а пересказывать копируя материал не хочется. Тем более, что многие приемы и детали упоминал ранее, только в контексте POSIX. Поэтому советую освежить в памяти предыдущие посты цикла. Увидите, что многие идеи и оптимизации придуманные для ОС были использованы в рантайме Go. Уровень абстракции выше, а проблемы и вызовы такие же😊
Внутри вас ждёт:
- Внутреннее представление
sync.Mutex.- Как выглядят
Fast path и Slow path в sync.Mutex (в том числе в ассемблере).- Как
sync.Mutex пытается обеспечить справедливость и избегать голодания.- Как происходит
Unlock.https://victoriametrics.com/blog/go-sync-mutex/
-----
Если вам понравился пост, поддержите его реакциями и комментариями. Мне важно увидеть вашу обратную связь. Спасибо!
P.S. Следующая публикация вероятнее всего будет на Хабре, выпуск намечен на следующую неделю. Будем как в прошлый раз писать свои велосипеды в погоне за эталонными реализациями, оценивать результаты бенчмарками и профилями😊
VictoriaMetrics
Go sync.Mutex: Normal and Starvation Mode
Mutex in Go has two main flows: Lock and Unlock and 2 modes: Normal and Starvation Mode. The state field of mutex is a 32-bit integer that represents the current state, it’s divided into multiple bits that encode various pieces of information about the mutex.
🔥20👍8❤2👎1
Concurrency, Synchronization and Consistency. Пост №19. Синхронизация в read-heavy сценариях. Read-Write Mutexes.
В прошлом посте мы познакомились с sync.Mutex в Go - примитивом рантайма обеспечивающим взаимное исключение. Он умный, у него как и у мьютекса ОС есть fast path / slow path. Кажется что все хорошо. Но есть задача с которой классическому мьютексу справляться тяжеловато.
Сделаем шаг назад и вспомним - зачем вообще нужна синхронизация? Все дело в том что нам нужно корректно рапространять ко всем потокам нашей программы события об изменении данных в памяти. Для этого нужны атомики, барьеры памяти. Если мы не будем синхронизироваться то каждый участник будет работать со своей копией данных, и перетирать друг друга, получая на выходе мусор.
Представим ситуацию, когда в нашей программе нет операций записи, а только чтение? Здравый смысл говорит нам что тогда нам вообще не нужны никакие примитивы - операции чтения являются потокобезопасными, не нужно ничего выдумывать.
А что если операции записи есть, но они редкие? Раз в минуту например? Или например 1 операция записи на 1000 чтений? Будет ли эффективной реализация когда мы защитимся обычным мьютексом? В общем случае нет, об эффективности речи быть и не может. Потому что мы будем работать в режиме не только эксклюзивной записи но и эксклюзивного чтения. Но ведь можем лучше, компьютер нас не ограничивает в параллельном чтении.
Для решения такой задачи были придуманы особенные примитивы синхронизации. Обычно с префиксом read-write в названии.
Примеры:
- pthread_rwlock_t в POSIX
- Read-Copy-Update, RCU в Linux
- sync.RWMutex в Golang
- ReentrantReadWriteLock в Java
Они реализованы внутри как связка обычного mutex и atomic переменных, ведущим учет читателей. Также важно отметить, что в языках в отличии от ОС эти примитивы могут не маппиться напрямую на примитивы ОС, потому что например в том же Go свои горутины, рантайм, и модель памяти. Примитив синхронизации должен им соответствовать. sync.RWMutex реализован на чистом Go с вкраплениями internal вызовов, можете проверить самостоятельно.
Семантика read-write блокировок
Концептуально такой мьютекс может вести себя одним из 3х способов:
- Пока память открыта на чтение, давать читателям беспрепятственный доступ. Писатели могут ждать сколько угодно.
- Как только появился хоть один писатель, никого больше не пускать. Все остальные могут простаивать.
- Не допускать простоев. Другими словами: независимо от действий других потоков, читатель или писатель должен пройти барьер за конечное время.
О том как достигать тех или иных гарантий поговорим отдельно.
На сегодня всё, в следующих постах поговорим о цене которую мы платим используя такой примитив, а также посмотрим на бенчмарках насколько оправдано его использование, возможны ли ситуации когда он делает только хуже.
-----
Если вам понравился пост, поддержите его реакциями и комментариями. Мне важно увидеть вашу обратную связь. Спасибо!
📖 Оглавление
В прошлом посте мы познакомились с sync.Mutex в Go - примитивом рантайма обеспечивающим взаимное исключение. Он умный, у него как и у мьютекса ОС есть fast path / slow path. Кажется что все хорошо. Но есть задача с которой классическому мьютексу справляться тяжеловато.
Сделаем шаг назад и вспомним - зачем вообще нужна синхронизация? Все дело в том что нам нужно корректно рапространять ко всем потокам нашей программы события об изменении данных в памяти. Для этого нужны атомики, барьеры памяти. Если мы не будем синхронизироваться то каждый участник будет работать со своей копией данных, и перетирать друг друга, получая на выходе мусор.
Представим ситуацию, когда в нашей программе нет операций записи, а только чтение? Здравый смысл говорит нам что тогда нам вообще не нужны никакие примитивы - операции чтения являются потокобезопасными, не нужно ничего выдумывать.
А что если операции записи есть, но они редкие? Раз в минуту например? Или например 1 операция записи на 1000 чтений? Будет ли эффективной реализация когда мы защитимся обычным мьютексом? В общем случае нет, об эффективности речи быть и не может. Потому что мы будем работать в режиме не только эксклюзивной записи но и эксклюзивного чтения. Но ведь можем лучше, компьютер нас не ограничивает в параллельном чтении.
Для решения такой задачи были придуманы особенные примитивы синхронизации. Обычно с префиксом read-write в названии.
Примеры:
- pthread_rwlock_t в POSIX
- Read-Copy-Update, RCU в Linux
- sync.RWMutex в Golang
- ReentrantReadWriteLock в Java
Они реализованы внутри как связка обычного mutex и atomic переменных, ведущим учет читателей. Также важно отметить, что в языках в отличии от ОС эти примитивы могут не маппиться напрямую на примитивы ОС, потому что например в том же Go свои горутины, рантайм, и модель памяти. Примитив синхронизации должен им соответствовать. sync.RWMutex реализован на чистом Go с вкраплениями internal вызовов, можете проверить самостоятельно.
Семантика read-write блокировок
Концептуально такой мьютекс может вести себя одним из 3х способов:
- Пока память открыта на чтение, давать читателям беспрепятственный доступ. Писатели могут ждать сколько угодно.
- Как только появился хоть один писатель, никого больше не пускать. Все остальные могут простаивать.
- Не допускать простоев. Другими словами: независимо от действий других потоков, читатель или писатель должен пройти барьер за конечное время.
О том как достигать тех или иных гарантий поговорим отдельно.
На сегодня всё, в следующих постах поговорим о цене которую мы платим используя такой примитив, а также посмотрим на бенчмарках насколько оправдано его использование, возможны ли ситуации когда он делает только хуже.
-----
Если вам понравился пост, поддержите его реакциями и комментариями. Мне важно увидеть вашу обратную связь. Спасибо!
📖 Оглавление
1🔥14👍6❤1
Concurrency, Synchronization and Consistency. Пост № 20. Масштабируемость Read-Write блокировки. False Sharing.
В прошлом посте мы с вами разобрались с основными моментами RW примитивов. Как и обещал - переходим к практике. У RW блокировок в классической реализации есть одна проблема из-за которой производительность операций чтения не будет расти линейно с ростом количества потоков.
Почему? Давайте вспомним самое начало цикла постов где мы обсуждали кеши CPU. В современном компьютере много ядер, работающих независимо и обеспечивающих параллелизм. Но когда они работают над общими данными они синхронизируются друг с другом обеспечивая когерентность кешей. Нужно это для того чтобы писать согласованные программы.
Причем здесь RW Mutex? Если вы внимательно изучали прошлый пост то увидели что я упомянул важную деталь - этот тип мьютекса ведет учет читателей. Именно эти данные и нужно каждый раз синхронизировать ядрам между собой чтобы программа вела себя корректно.
Это false sharing - ситуация, когда несколько потоков одновременно обращаются к переменным, которые находятся в одной кеш-линии. Кеш-линия - это минимальная единица данных, которая копируется из оперативной памяти в кеш процессора.
Что происходит на практике. Пошаговый алгоритм.
Представим что у нас N ядер и N потоков.
- В кеше каждого процессора есть данные нашего мьютекса.
- Ядро Х дает потоку доступ на чтение, увеличивает количество читателей на 1.
- Все остальные ядра по протоколу когерентности получают уведомления о том что данные изменены и их нужно перечитать. В собственном кеше каждое ядро помечает данные "грязными".
- Ядро при попытке поработать с мьютексом получает cache miss, и идет читать данные из основной памяти. Чтение из L1 кеша - 0.5ns а из основной памяти - 100ns.
Следствие - наша программа вместо полезной работы будет заниматься пинг-понгом и синхронизацией кешей. Чем больше ядер у машины, тем более явно это будет проявляться.
Выводы
Read Write блокировки только на первый взгляд кажутся очевидным выбором при написании highload программ. Обязательно экспериментируйте с ними и пробуйте сначала воспользоваться стандартным мьютексом, может оказаться что он будет работать лучше.
В следующем посте на примерах кода попробуем вывести определенные закономерности и лайфхаки когда использовать RWMutex а когда обычный. А еще дальше расскажу о том какие трюки применяют умельцы работающие в супер нагруженных продуктах чтобы выжимать максимум из железа.
-----
Если вам понравился пост, поддержите его реакциями и комментариями. Мне важно увидеть вашу обратную связь. Спасибо!
📖 Оглавление
В прошлом посте мы с вами разобрались с основными моментами RW примитивов. Как и обещал - переходим к практике. У RW блокировок в классической реализации есть одна проблема из-за которой производительность операций чтения не будет расти линейно с ростом количества потоков.
Почему? Давайте вспомним самое начало цикла постов где мы обсуждали кеши CPU. В современном компьютере много ядер, работающих независимо и обеспечивающих параллелизм. Но когда они работают над общими данными они синхронизируются друг с другом обеспечивая когерентность кешей. Нужно это для того чтобы писать согласованные программы.
Причем здесь RW Mutex? Если вы внимательно изучали прошлый пост то увидели что я упомянул важную деталь - этот тип мьютекса ведет учет читателей. Именно эти данные и нужно каждый раз синхронизировать ядрам между собой чтобы программа вела себя корректно.
Это false sharing - ситуация, когда несколько потоков одновременно обращаются к переменным, которые находятся в одной кеш-линии. Кеш-линия - это минимальная единица данных, которая копируется из оперативной памяти в кеш процессора.
Что происходит на практике. Пошаговый алгоритм.
Представим что у нас N ядер и N потоков.
- В кеше каждого процессора есть данные нашего мьютекса.
- Ядро Х дает потоку доступ на чтение, увеличивает количество читателей на 1.
- Все остальные ядра по протоколу когерентности получают уведомления о том что данные изменены и их нужно перечитать. В собственном кеше каждое ядро помечает данные "грязными".
- Ядро при попытке поработать с мьютексом получает cache miss, и идет читать данные из основной памяти. Чтение из L1 кеша - 0.5ns а из основной памяти - 100ns.
Следствие - наша программа вместо полезной работы будет заниматься пинг-понгом и синхронизацией кешей. Чем больше ядер у машины, тем более явно это будет проявляться.
Выводы
Read Write блокировки только на первый взгляд кажутся очевидным выбором при написании highload программ. Обязательно экспериментируйте с ними и пробуйте сначала воспользоваться стандартным мьютексом, может оказаться что он будет работать лучше.
В следующем посте на примерах кода попробуем вывести определенные закономерности и лайфхаки когда использовать RWMutex а когда обычный. А еще дальше расскажу о том какие трюки применяют умельцы работающие в супер нагруженных продуктах чтобы выжимать максимум из железа.
-----
Если вам понравился пост, поддержите его реакциями и комментариями. Мне важно увидеть вашу обратную связь. Спасибо!
📖 Оглавление
1👍19🔥13
Пока готовлю завершающие посты цикла по Concurrency мне попался великолепный материал по Goшным каналам. Кажется что материалов тьма уже но именно эта статья на меня произвела сильное впечатление потому что в ней есть:
- Исторические предпосылки к создания примитива. Communicating Sequential Processes (CSP).
- Как устроена структура данных примитива. hchan. Как это выглядит с точки зрения памяти (memory layout).
- Структура
- Пошаговые алгоритмы записи в канал / чтения из канала / закрытие канала.
- Функция select.
- Как работает копирование данных канала между горутинами.
- Связь каналов с моделью памяти. Синхронизация.
- Связь с планировщиком.
Обилие примеров для лучшего понимания сложных материй рантайма.
В общем, рекомендую. Подойдет как для подготовки к собесам так и для того чтобы лучше разобраться. Уверен, откроете для себя что-то новое.
- Исторические предпосылки к создания примитива. Communicating Sequential Processes (CSP).
- Как устроена структура данных примитива. hchan. Как это выглядит с точки зрения памяти (memory layout).
- Структура
sudog ("suspended goroutine"). Зачем нужна, какие проблемы решает, почему именно так.- Пошаговые алгоритмы записи в канал / чтения из канала / закрытие канала.
- Функция select.
- Как работает копирование данных канала между горутинами.
- Связь каналов с моделью памяти. Синхронизация.
- Связь с планировщиком.
Обилие примеров для лучшего понимания сложных материй рантайма.
В общем, рекомендую. Подойдет как для подготовки к собесам так и для того чтобы лучше разобраться. Уверен, откроете для себя что-то новое.
Gaborkoos
Go Channels: A Runtime Internals Deep Dive
A deep-dive into Go's channel internals.
🔥9👍4❤1
Евгений Козлов пишет про IT
Concurrency, Synchronization and Consistency. Пост № 20. Масштабируемость Read-Write блокировки. False Sharing. В прошлом посте мы с вами разобрались с основными моментами RW примитивов. Как и обещал - переходим к практике. У RW блокировок в классической…
Concurrency, Synchronization and Consistency. Пост № 21. RWMutex vs Mutex.
Ранее я написал посты про оба вида мьютексов, про внутренности и особенности. Настало время их сравнить.
Перед тем как что-то сравнивать нужны ориентиры. Нам как разработчикам важно:
- Чтобы код был простым.
- Чтобы код был быстрым.
- Чтобы код потреблял как можно меньше ресурсов.
🔵Mutex
🧠Простота. Здесь однозначно +. Внутренняя структура проще + публичное API для разработчика очевидно (Lock / Unlock).
🚀 Скорость. Здесь есть над чем порассуждать.
➕ Внутри Mutex уже есть оптимизации (Fast / Slow path / Futex). Если код написан хорошо то мьютекса будет достаточно для эффективной работы.
➕Масштабируемость. Если в программе увеличить количество потоков и добавить ресурса то код будет примерно линейно масштабироваться (для операций чтения, для операций записи нужно будет синхронизировать кеши ядер CPU).
➖Нельзя распараллелить thread-safe операции чтения. Mutex это "эксклюзивное владение" ресурсом.
➖Как следствие - рост latency если критическая секция кода долгая. В случае короткой секции горутины / потоки не засыпают, а активно ждут своей очереди. Если не дождались - планировщик усыпит горутину, чтобы выделить ресурсы кому-то еще. Все это накладные расходы влияющие не столько на ресурсы сколько на время работы программы.
➖Помимо этого - рост latency если горутин / потоков много. Даже если критическая секция короткая потоки и горутины будут усыпляться планировщиком потому что на всех не хватает ядер. Накладные расходы на пробуждение / усыпление / взаимодействие с ОС.
💰Ресурсы. В худшем случае наши горутины и потоки спят. Ресурсы могут потреблять другие потоки и программы. В остальном mutex полагается на рантайм (языка или ос).
🔵RWMutex
🧠Простота. Тут однозначно минус. RWMutex это умная обертка над обычным мьютексом. Добавляется несколько atomic переменных чтобы в read cценарии избавиться от эксклюзивного владения и параллелить по ядрам чтения.
🚀 Скорость. Здесь у RWMutex есть потенциал уделать обычный Mutex. В read-heavy сценариях горутины / потоки практически никогда не будут засыпать и блокироваться. А значит программа будет быстрее.
Также при добавлении ядер и потоков будет выше параллелизм -> еще ниже latency. Количество читающих горутин может исчисляться тысячами и они не будут блокироваться на мьютексе если хотят что-то прочитать.
Но - это только теория. На практике RWMutex может сделать хуже. Каждый вызов функций RWMutex изменяет atomic переменные. И чем больше у нас читателей / потоков и как следствие ядер CPU тем больше времени будет уходить на синхронизацию кешей процессора. Обычный Mutex лишен этого недостатка.
В Go даже есть issue с обсуждением этой проблемы
💰Ресурсы. Как упомянул ранее RWMutex потребляет больше чем обычный mutex потому что внутри него больше логики и данных. Это цена которую мы платим. Чем больше читателей и ядер тем больше "набегает" .
🔵Выводы. Что же делать?
№1 - Всегда начинать с Mutex. Он прост, адаптируется под изменение конфигурации (потоки / ядра). Если код написан правильно и критические секции короткие программа будет достаточно быстрой и эффективной.
№2 - Помнить про трейдофф latency vs throughput.
- Mutex - это low cpu / high latency.
- RWMutex - high cpu / low latency (до поры до времени).
№3 - Помнить о том что RWMutex имеет bottleneck
RWMutex спасает лишь до поры до времени. Однажды он станет вредителем а не помощником. Не упустите этот момент.
№4 - Метрики, профили, бенчмарки это наше всё.
Надеюсь мне удалось показать, что RWMutex очень специфичная вещь с кучей особенностей, и его сложно советовать по умолчанию. Внедрять его без понимания последствий может быть чревато ухудшением производительности.
Чтобы это понимание приобрести нужно время на сбор профилей, бенчмарки, и нагрузочные тесты. Они помогут понять будет ли иметь смысл внедрение RWMutex.
Преждевременная оптимизация - зло, а преждевременная оптимизация без метрик - абсолютное зло😈
Спасибо, что читали, буду рад вашей обратной связи и реакциям!
📖 Оглавление
Ранее я написал посты про оба вида мьютексов, про внутренности и особенности. Настало время их сравнить.
Перед тем как что-то сравнивать нужны ориентиры. Нам как разработчикам важно:
- Чтобы код был простым.
- Чтобы код был быстрым.
- Чтобы код потреблял как можно меньше ресурсов.
🔵Mutex
🧠Простота. Здесь однозначно +. Внутренняя структура проще + публичное API для разработчика очевидно (Lock / Unlock).
➕ Внутри Mutex уже есть оптимизации (Fast / Slow path / Futex). Если код написан хорошо то мьютекса будет достаточно для эффективной работы.
➕Масштабируемость. Если в программе увеличить количество потоков и добавить ресурса то код будет примерно линейно масштабироваться (для операций чтения, для операций записи нужно будет синхронизировать кеши ядер CPU).
➖Нельзя распараллелить thread-safe операции чтения. Mutex это "эксклюзивное владение" ресурсом.
➖Как следствие - рост latency если критическая секция кода долгая. В случае короткой секции горутины / потоки не засыпают, а активно ждут своей очереди. Если не дождались - планировщик усыпит горутину, чтобы выделить ресурсы кому-то еще. Все это накладные расходы влияющие не столько на ресурсы сколько на время работы программы.
➖Помимо этого - рост latency если горутин / потоков много. Даже если критическая секция короткая потоки и горутины будут усыпляться планировщиком потому что на всех не хватает ядер. Накладные расходы на пробуждение / усыпление / взаимодействие с ОС.
💰Ресурсы. В худшем случае наши горутины и потоки спят. Ресурсы могут потреблять другие потоки и программы. В остальном mutex полагается на рантайм (языка или ос).
🔵RWMutex
🧠Простота. Тут однозначно минус. RWMutex это умная обертка над обычным мьютексом. Добавляется несколько atomic переменных чтобы в read cценарии избавиться от эксклюзивного владения и параллелить по ядрам чтения.
Также при добавлении ядер и потоков будет выше параллелизм -> еще ниже latency. Количество читающих горутин может исчисляться тысячами и они не будут блокироваться на мьютексе если хотят что-то прочитать.
Но - это только теория. На практике RWMutex может сделать хуже. Каждый вызов функций RWMutex изменяет atomic переменные. И чем больше у нас читателей / потоков и как следствие ядер CPU тем больше времени будет уходить на синхронизацию кешей процессора. Обычный Mutex лишен этого недостатка.
В Go даже есть issue с обсуждением этой проблемы
💰Ресурсы. Как упомянул ранее RWMutex потребляет больше чем обычный mutex потому что внутри него больше логики и данных. Это цена которую мы платим. Чем больше читателей и ядер тем больше "набегает" .
🔵Выводы. Что же делать?
№1 - Всегда начинать с Mutex. Он прост, адаптируется под изменение конфигурации (потоки / ядра). Если код написан правильно и критические секции короткие программа будет достаточно быстрой и эффективной.
№2 - Помнить про трейдофф latency vs throughput.
- Mutex - это low cpu / high latency.
- RWMutex - high cpu / low latency (до поры до времени).
№3 - Помнить о том что RWMutex имеет bottleneck
RWMutex спасает лишь до поры до времени. Однажды он станет вредителем а не помощником. Не упустите этот момент.
№4 - Метрики, профили, бенчмарки это наше всё.
Надеюсь мне удалось показать, что RWMutex очень специфичная вещь с кучей особенностей, и его сложно советовать по умолчанию. Внедрять его без понимания последствий может быть чревато ухудшением производительности.
Чтобы это понимание приобрести нужно время на сбор профилей, бенчмарки, и нагрузочные тесты. Они помогут понять будет ли иметь смысл внедрение RWMutex.
Преждевременная оптимизация - зло, а преждевременная оптимизация без метрик - абсолютное зло😈
Спасибо, что читали, буду рад вашей обратной связи и реакциям!
📖 Оглавление
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9🔥5❤1
Concurrency, Synchronization and Consistency. Пост № 22. Пример False Sharing в многопоточной программе.
В последних постах было много текста и сложных материй. И не было примеров кода. Продемонстрировать RWMutex bottleneck довольно сложно(нужен процессор с большим количеством ядер, чем больше тем лучше). Но у меня все равно появилась идея как продемонстрировать False Sharing в Concurrency.
🔵 Show me code
Представим что у нас есть программа с atomic переменной, которую мы хотим увеличивать.
Напишем две версии программы.
- последовательная, в одной горутине работаем по очереди с каждым атомиком.
- параллельная, каждой горутине выдаем свой атомик.
🔵 Последовательная версия
Статистика по времени работы (
Итог: Загрузили одно ядро на 1 секунду. Ничего особенного.
🔵 Параллельная версия
Статистика
Итог: 5 ядер загрузили и при этом программа работала 7+ СЕКУНД. Не такого результата мы конечно ожидали.
🔵 Что же такое этот False Sharing?
Запустим код:
Вывод:
Видим что адреса переменных находятся рядом, в 4 байтах друг от друга. И это причина по которой многопоточная версия работает хуже.
Несмотря на то что у нас каждой горутине выделена своя переменная и в коде они не зависят друг от друга у нас есть есть точка синхронизации - кеши CPU. Каждое ядро кеширует не только значение своей переменной, но и значения остальных переменных - потому что кеширование осуществляется блоками, обычно по 64 Bytes. Их называют кеш-линиями. Отсюда и неявные доп. расходы на синхронизацию (хотя в коде у нас нет ничего подобного) и как следствие увеличение времени работы.
🔵 Как починить False Sharing?
Чтобы горутины работали независимо,нужно чтобы в одну кеш линию помещалась только одна переменная. Обернем в структуру и добавим ей байт до размера кеш линии (техника называется padding).
Статистика:
🔵 Выводы
Мы смогли добиться эффективного параллелизма, но заплатили цену - явное "подстраивание" кода программы под железо. В обычной разработке мы редко прибегаем к подобным трюкам. Нам это попросту не нужно, так как наши программы чаще всего io bound. Да и не в каждом продукте достаточная нагрузка.
А вот для больших компаний подобные вещи - экономия сотен тысяч долларов. И если зарплата программиста становится менее значимым фактором чем затраты на инфру внесение вот таких изменений в программу и глубокое профилирование становится оправданным шагом. Для разработчиков СУБД, языков программирования, системных программистов такие трюки это часть жизни.
Спасибо что читали, буду рад вашим реакциям и комментариям!
📖 Оглавление
В последних постах было много текста и сложных материй. И не было примеров кода. Продемонстрировать RWMutex bottleneck довольно сложно(нужен процессор с большим количеством ядер, чем больше тем лучше). Но у меня все равно появилась идея как продемонстрировать False Sharing в Concurrency.
🔵 Show me code
Представим что у нас есть программа с atomic переменной, которую мы хотим увеличивать.
func worker(v *atomic.Int32, wg *sync.WaitGroup) {
for i := 0; i < 100000000; i++ {
v.Add(1)
}
if wg != nil {
wg.Done()
}
}
Напишем две версии программы.
- последовательная, в одной горутине работаем по очереди с каждым атомиком.
- параллельная, каждой горутине выдаем свой атомик.
🔵 Последовательная версия
var a, b, c, d, e atomic.Int32
list := []*atomic.Int32{&a, &b, &c, &d, &e}
for _, atom := range list {
worker(atom, nil)
}
Статистика по времени работы (
time ./main)
0.84s user
0.01s system
99% cpu
0.851 total
Итог: Загрузили одно ядро на 1 секунду. Ничего особенного.
🔵 Параллельная версия
var a, b, c, d, e atomic.Int32
list := []*atomic.Int32{&a, &b, &c, &d, &e}
var wg sync.WaitGroup
wg.Add(5)
for _, atom := range list {
go func(atom *atomic.Int32) {
worker(atom, &wg)
}(atom)
}
wg.Wait()
Статистика
35.66s user
0.08s system
470% cpu
7.588 total
Итог: 5 ядер загрузили и при этом программа работала 7+ СЕКУНД. Не такого результата мы конечно ожидали.
🔵 Что же такое этот False Sharing?
Запустим код:
var a, b, c, d, e atomic.Int32
fmt.Printf("address of a: %p\n", &a)
fmt.Printf("address of b: %p\n", &b)
fmt.Printf("address of c: %p\n", &c)
fmt.Printf("address of d: %p\n", &d)
fmt.Printf("address of e: %p\n", &e)
Вывод:
address of a: 0x140000100a0
address of b: 0x140000100a4
address of c: 0x140000100a8
address of d: 0x140000100ac
address of e: 0x140000100b0
Видим что адреса переменных находятся рядом, в 4 байтах друг от друга. И это причина по которой многопоточная версия работает хуже.
Несмотря на то что у нас каждой горутине выделена своя переменная и в коде они не зависят друг от друга у нас есть есть точка синхронизации - кеши CPU. Каждое ядро кеширует не только значение своей переменной, но и значения остальных переменных - потому что кеширование осуществляется блоками, обычно по 64 Bytes. Их называют кеш-линиями. Отсюда и неявные доп. расходы на синхронизацию (хотя в коде у нас нет ничего подобного) и как следствие увеличение времени работы.
🔵 Как починить False Sharing?
Чтобы горутины работали независимо,нужно чтобы в одну кеш линию помещалась только одна переменная. Обернем в структуру и добавим ей байт до размера кеш линии (техника называется padding).
type PaddedInt32 struct {
value atomic.Int32
_ [60]byte
}
func worker_padded(v *PaddedInt32, wg *sync.WaitGroup) {
for i := 0; i < 100000000; i++ {
v.value.Add(1)
}
if wg != nil {
wg.Done()
}
}
func main() {
var a, b, c, d, e PaddedInt32
list := []*PaddedInt32{&a, &b, &c, &d, &e}
var wg sync.WaitGroup
wg.Add(5)
for _, atom := range list {
go func(atom *PaddedInt32) {
worker_padded(atom, &wg)
}(atom)
}
wg.Wait()
}
Статистика:
1.23s user
0.01s system
447% cpu
0.275 total
🔵 Выводы
Мы смогли добиться эффективного параллелизма, но заплатили цену - явное "подстраивание" кода программы под железо. В обычной разработке мы редко прибегаем к подобным трюкам. Нам это попросту не нужно, так как наши программы чаще всего io bound. Да и не в каждом продукте достаточная нагрузка.
А вот для больших компаний подобные вещи - экономия сотен тысяч долларов. И если зарплата программиста становится менее значимым фактором чем затраты на инфру внесение вот таких изменений в программу и глубокое профилирование становится оправданным шагом. Для разработчиков СУБД, языков программирования, системных программистов такие трюки это часть жизни.
Спасибо что читали, буду рад вашим реакциям и комментариям!
📖 Оглавление
Telegram
Евгений Козлов пишет про IT
Concurrency, Synchronization and Consistency.
🔹Основы. Железо
- Архитектура Фон-Неймана
- Узкое место архитектуры Фон-Неймана
- Когерентность кеша. Основы
- Контроллеры когерентности
- Протокол когерентности MSI
- Блокировки, атомарные операции
- Что такое…
🔹Основы. Железо
- Архитектура Фон-Неймана
- Узкое место архитектуры Фон-Неймана
- Когерентность кеша. Основы
- Контроллеры когерентности
- Протокол когерентности MSI
- Блокировки, атомарные операции
- Что такое…
🔥20👍4❤1
Concurrency, Synchronization and Consistency. Пост № 23. Priority Inversion или баг случившийся на Марсе (реально)
Что-то я увлекся рассказами про мьютексы и совсем забыл что не довел рассказ про планирование потоков и приоритеты до конца. Возвращаю должок, рассмотрим интересную задачу из реальной жизни.
🔵 Ситуация
У нас есть 3 потока и каждому назначена своя задача (псевдокод для простоты):
Что происходит:
- L стартует первым -> захватывает mutex
- H стартует вторым -> пытается захватить mutex -> блокируется, ждёт L
- M стартует третьим -> вытесняет L (приоритет выше) -> крутится на CPU
Итог:
- L не получает CPU, не может освободить mutex.
- H ждёт L, но L вытеснен M.
Следствие: Неэффективная работа программы. Как вы уже поняли из названия эта ситуация и есть инверсия приоритетов - проблема в многозадачных системах, когда задача с высоким приоритетом вынуждена ждать выполнения задачи с низким приоритетом, владеющей необходимым ей ресурсом (мьютексом).
На первый взгляд выглядит странно. В обычных задачах на работе нам не нужно знать про приоритеты потоков. Плюс вытесняющий планировщик выручает переключая контекст и выделяя всем время поработать. Вроде проблема, а вроде и нет🙂
Ответ: Если бы такая ситуация случилась на каком нибудь устройстве от которого ожидают работы в реальном времени то это могла бы быть катастрофа. И такие провалы имеют место в реальности, например на Марсе в 1997м году. Рассказывать долго не хочу, советую статью на хабре с иллюстрациями и контекстом.
🔵 Как разрешить Priority Inversion?
1️⃣ Запрет прерываний в критических секциях
Первое что приходит в голову - запретить прерывать поток находящийся в критической секции (помним что у нас вытесняющее планирование и ОС может прервать поток в любой момент). Но доступна такая магия только в kernel space, поэтому и используется трюк обычно:
- В драйверах для атомарного доступа к hardware registers.
- В критических секциях ядра, чтобы избежать прерывания и race conditions.
Нам нужны механизмы влияния в user-space, поэтому идем дальше.
2️⃣ Протокол пороговых приоритетов (priority ceiling protocol, PCP)
- Каждый mutex имеет "потолочный приоритет" - максимальный приоритет всех потоков, которые могут его захватить.
- Когда поток захватывает такой mutex его приоритет временно повышается до потолка mutex. Это предотвращает вытеснение потока средними по приоритету задачами.
- Когда поток освобождает mutex приоритет возвращается к исходному.
Реализация в POSIX:
3️⃣Протокол наследования приоритетов (priority inheritance protocol, PIP)
Когда H ждёт mutex, который удерживает L:
- L временно получает приоритет H, пока не освободит mutex.
- После unlock приоритет L возвращается к исходному.
Реализация в POSIX:
🔵 Что выбрать, PIP или PCP?
- PIP защищает от priority inversion, но допускает deadlock. Довольно просто реализуется, есть везде.
- PCP более строгий, предсказуемый, предотвращает priority inversion и deadlock. Его основной минус - избыточное повышение приоритета. Сложнее в реализации и поддержке.
В реальной жизни используют PIP для стандартных приложений на Linux/RTLinux и PCP для safety-critical embedded или RTOS.
—————
Фух, думаю на этом остановиться. Основные проблемы Concurrency (Deadlock, Race Condition, Readers-Writers, Busy Waiting, Priority Inversion) в рамках цикла постов мы рассмотрели.
Осталось несколько тем и цикл можно завершать. Спасибо что читали, буду рад вашим реакциям и комментариям!
📖 Оглавление
Что-то я увлекся рассказами про мьютексы и совсем забыл что не довел рассказ про планирование потоков и приоритеты до конца. Возвращаю должок, рассмотрим интересную задачу из реальной жизни.
🔵 Ситуация
У нас есть 3 потока и каждому назначена своя задача (псевдокод для простоты):
Thread L (low priority):
lock(mutex)
do_some_work()
unlock(mutex)
Thread H (high priority):
lock(mutex)
critical_work()
unlock(mutex)
Thread M (medium priority):
while true:
do_cpu_work()
Что происходит:
- L стартует первым -> захватывает mutex
- H стартует вторым -> пытается захватить mutex -> блокируется, ждёт L
- M стартует третьим -> вытесняет L (приоритет выше) -> крутится на CPU
Итог:
- L не получает CPU, не может освободить mutex.
- H ждёт L, но L вытеснен M.
Следствие: Неэффективная работа программы. Как вы уже поняли из названия эта ситуация и есть инверсия приоритетов - проблема в многозадачных системах, когда задача с высоким приоритетом вынуждена ждать выполнения задачи с низким приоритетом, владеющей необходимым ей ресурсом (мьютексом).
На первый взгляд выглядит странно. В обычных задачах на работе нам не нужно знать про приоритеты потоков. Плюс вытесняющий планировщик выручает переключая контекст и выделяя всем время поработать. Вроде проблема, а вроде и нет🙂
Ответ: Если бы такая ситуация случилась на каком нибудь устройстве от которого ожидают работы в реальном времени то это могла бы быть катастрофа. И такие провалы имеют место в реальности, например на Марсе в 1997м году. Рассказывать долго не хочу, советую статью на хабре с иллюстрациями и контекстом.
🔵 Как разрешить Priority Inversion?
1️⃣ Запрет прерываний в критических секциях
Первое что приходит в голову - запретить прерывать поток находящийся в критической секции (помним что у нас вытесняющее планирование и ОС может прервать поток в любой момент). Но доступна такая магия только в kernel space, поэтому и используется трюк обычно:
- В драйверах для атомарного доступа к hardware registers.
- В критических секциях ядра, чтобы избежать прерывания и race conditions.
Нам нужны механизмы влияния в user-space, поэтому идем дальше.
2️⃣ Протокол пороговых приоритетов (priority ceiling protocol, PCP)
- Каждый mutex имеет "потолочный приоритет" - максимальный приоритет всех потоков, которые могут его захватить.
- Когда поток захватывает такой mutex его приоритет временно повышается до потолка mutex. Это предотвращает вытеснение потока средними по приоритету задачами.
- Когда поток освобождает mutex приоритет возвращается к исходному.
Реализация в POSIX:
pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_PROTECT);
3️⃣Протокол наследования приоритетов (priority inheritance protocol, PIP)
Когда H ждёт mutex, который удерживает L:
- L временно получает приоритет H, пока не освободит mutex.
- После unlock приоритет L возвращается к исходному.
Реализация в POSIX:
pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT);
🔵 Что выбрать, PIP или PCP?
- PIP защищает от priority inversion, но допускает deadlock. Довольно просто реализуется, есть везде.
- PCP более строгий, предсказуемый, предотвращает priority inversion и deadlock. Его основной минус - избыточное повышение приоритета. Сложнее в реализации и поддержке.
В реальной жизни используют PIP для стандартных приложений на Linux/RTLinux и PCP для safety-critical embedded или RTOS.
—————
Фух, думаю на этом остановиться. Основные проблемы Concurrency (Deadlock, Race Condition, Readers-Writers, Busy Waiting, Priority Inversion) в рамках цикла постов мы рассмотрели.
Осталось несколько тем и цикл можно завершать. Спасибо что читали, буду рад вашим реакциям и комментариям!
📖 Оглавление
🔥14👍3
Провожаем 2025й, встречаем 2026й
30е числа декабря это время когда многие подводят итоги, ставят цели и формируют планы на будущее. Признаюсь честно - далек от этого. Я редко ставлю себе детализированные планы, скорее ставлю себе некоторые направления в которых двигаюсь. Многие вещи и идеи могут появиться по ходу и оказаться не менее интересными и ценными чем то что изначально запланировал.
Мой 2025й был насыщенным на разные события. Даже слишком. А ещё оказался наверное самым сложным за всю мою "взрослую жизнь". Сижу, пишу пост и понимаю - отдал все свои силы без остатка.
Несмотря на это подвести итоги нужно. Усталость пройдет, а вот воспоминания и события могут стереться из памяти со временем. Мне этого не хочется🙂 Да и я вас в этом году не очень то и баловал постами про себя, очень много хардовых хардов, и почти ничего личного. Поэтому пусть будет.
———
Q1 2025
🎉 Отметил 30летие в кругу близких людей. Родители провели тайную операцию и приехали втихую ко мне в Питер.
📺 Был ведущим книжного клуба. Вместе с ребятами провели 10 встреч, прочитали книгу Fundamentals of Data Engineering.
✍️Закончил первый по настоящему долгий цикл постов про Concurrency (писал на протяжении 6 месяцев). Очень глубоко прокачался сам и старался прокачивать вас.
🎒 В первый раз в жизни пошел учиться на IT курс. Получил сертификат. В процессе писал заметки в канале.
🎁 В первый раз в жизни по настоящему выиграл в лотерею. ФК Спартак Москва подарил мне PS 5 PRO 🎮
Q2 2025
🏠 Достиг цели к которой долго шел - купил большую квартиру в СПБ. Залез в долги, но к концу года расплатился😁
📺 Сходил на подкаст к Саше Поломодову. До этого побывал в гостях на подкасте у Кирилла Мокевнина
✈️ Путешествия: побывал в Екатеринбурге.
Q3 2025
🎤Выступил на ИТ фестивале Сезон Кода в СПБ и на Yandex Scale c докладом про технические вызовы преодоленные за годы работы над Statist.
✍️Начал новый цикл постов по Concurrency посвященный согласованности и синхронизации.
✈️Путешествие в Великий Новгород. Памятник тысячелетию Руси оставил сильное впечатление.
✈️Путешествие в Мурманск. Одно из самых запоминающихся путеществий в жизни. Северное сияние, путешествие по островам на вертолете.
Q4 2025
💪 Провел своей команде второе Performance Review как руководитель. Поучаствовал в реорганизации процессов и орг. структуры нашего отдела. В формировании целей и дальнейшей стратегии.
✈️Путешествия: Ереван. Армения оставила приятные впечатления, рассчитываю посетить еще как минимум один раз🙂
🎒Выступил в родной школе (п.г.т. Красная Гора Брянской области) с докладом о программировании и карьере разработчика.
✍️ Написал 2 статьи на Хабре.
———
Отдельно хочется отметить проект на работе:
📈 За год мы выросли примерно в 5 раз по нагрузке и объемам и это требовало от меня и команды сопоставимого роста по навыкам и усилиям. Много чего оптимизировали и ускорили, чтобы оставаться такими же эффективными и для клиентов все работало также классно как и в 2024м.
👨💼Я примерял на себя новые зоны ответственности. Техническое лидерство в продукте. Коммуникация с основными потребителями. Планирование миграций, Capacity Management.
🤼♂️Несколько членов моей команды получили повышения. Считаю это хорошим знаком и показателем, что проект важный и ценный, и в нем есть где себя проявить.
———
Чего хочу пожелать себе в 2026м:
- Постараться находить больше времени и сил на посты в канале.
- Поработать над подачей материала, чтобы зарождалась дискуссия. Мне этого очень не хватает. Пока не знаю как этого достичь без неискреннего кликбейта.
- Чаще говорить слово нет. Беречь свое время и силы. Проанализировать куда уходил ресурс в 2025м году, и не допустить повторения в 2026м.
- Вернуться в менторство. Пауза затянулась и мне это не нравится.
———
Фух, хорош😁
Дорогие подписчики, я поздравляю Вас с наступающим Новым годом и Рождеством. Желаю достижения всех намеченных целей, при этом с удовольствием и наслаждением от процесса.
Вкусно кушайте, набирайтесь сил в кругу близких людей, чтобы в новом году всё сложилось волшебно!
30е числа декабря это время когда многие подводят итоги, ставят цели и формируют планы на будущее. Признаюсь честно - далек от этого. Я редко ставлю себе детализированные планы, скорее ставлю себе некоторые направления в которых двигаюсь. Многие вещи и идеи могут появиться по ходу и оказаться не менее интересными и ценными чем то что изначально запланировал.
Мой 2025й был насыщенным на разные события. Даже слишком. А ещё оказался наверное самым сложным за всю мою "взрослую жизнь". Сижу, пишу пост и понимаю - отдал все свои силы без остатка.
Несмотря на это подвести итоги нужно. Усталость пройдет, а вот воспоминания и события могут стереться из памяти со временем. Мне этого не хочется🙂 Да и я вас в этом году не очень то и баловал постами про себя, очень много хардовых хардов, и почти ничего личного. Поэтому пусть будет.
———
Q1 2025
🎉 Отметил 30летие в кругу близких людей. Родители провели тайную операцию и приехали втихую ко мне в Питер.
📺 Был ведущим книжного клуба. Вместе с ребятами провели 10 встреч, прочитали книгу Fundamentals of Data Engineering.
✍️Закончил первый по настоящему долгий цикл постов про Concurrency (писал на протяжении 6 месяцев). Очень глубоко прокачался сам и старался прокачивать вас.
🎒 В первый раз в жизни пошел учиться на IT курс. Получил сертификат. В процессе писал заметки в канале.
🎁 В первый раз в жизни по настоящему выиграл в лотерею. ФК Спартак Москва подарил мне PS 5 PRO 🎮
Q2 2025
🏠 Достиг цели к которой долго шел - купил большую квартиру в СПБ. Залез в долги, но к концу года расплатился😁
📺 Сходил на подкаст к Саше Поломодову. До этого побывал в гостях на подкасте у Кирилла Мокевнина
✈️ Путешествия: побывал в Екатеринбурге.
Q3 2025
🎤Выступил на ИТ фестивале Сезон Кода в СПБ и на Yandex Scale c докладом про технические вызовы преодоленные за годы работы над Statist.
✍️Начал новый цикл постов по Concurrency посвященный согласованности и синхронизации.
✈️Путешествие в Великий Новгород. Памятник тысячелетию Руси оставил сильное впечатление.
✈️Путешествие в Мурманск. Одно из самых запоминающихся путеществий в жизни. Северное сияние, путешествие по островам на вертолете.
Q4 2025
💪 Провел своей команде второе Performance Review как руководитель. Поучаствовал в реорганизации процессов и орг. структуры нашего отдела. В формировании целей и дальнейшей стратегии.
✈️Путешествия: Ереван. Армения оставила приятные впечатления, рассчитываю посетить еще как минимум один раз🙂
🎒Выступил в родной школе (п.г.т. Красная Гора Брянской области) с докладом о программировании и карьере разработчика.
✍️ Написал 2 статьи на Хабре.
———
Отдельно хочется отметить проект на работе:
📈 За год мы выросли примерно в 5 раз по нагрузке и объемам и это требовало от меня и команды сопоставимого роста по навыкам и усилиям. Много чего оптимизировали и ускорили, чтобы оставаться такими же эффективными и для клиентов все работало также классно как и в 2024м.
👨💼Я примерял на себя новые зоны ответственности. Техническое лидерство в продукте. Коммуникация с основными потребителями. Планирование миграций, Capacity Management.
🤼♂️Несколько членов моей команды получили повышения. Считаю это хорошим знаком и показателем, что проект важный и ценный, и в нем есть где себя проявить.
———
Чего хочу пожелать себе в 2026м:
- Постараться находить больше времени и сил на посты в канале.
- Поработать над подачей материала, чтобы зарождалась дискуссия. Мне этого очень не хватает. Пока не знаю как этого достичь без неискреннего кликбейта.
- Чаще говорить слово нет. Беречь свое время и силы. Проанализировать куда уходил ресурс в 2025м году, и не допустить повторения в 2026м.
- Вернуться в менторство. Пауза затянулась и мне это не нравится.
———
Фух, хорош😁
Дорогие подписчики, я поздравляю Вас с наступающим Новым годом и Рождеством. Желаю достижения всех намеченных целей, при этом с удовольствием и наслаждением от процесса.
Вкусно кушайте, набирайтесь сил в кругу близких людей, чтобы в новом году всё сложилось волшебно!
1🔥25❤7👍1
