Как стать автором
Обновить
7.9

Параллельное программирование *

Распараллеливаем вычисления

Сначала показывать
Порог рейтинга
Уровень сложности

Как я за месяц написал интерпретируемый язык программирования на Python

Уровень сложностиПростой
Время на прочтение7 мин
Количество просмотров20K

Привет, Хабр! В этой статье я хотел бы поделиться опытом создания своего языка программирования.

Предыстория

Мне 14. Обучаясь на втором году Яндекс Лицея, нужно было написать несколько проектов. Первым из них стал проект на PyQT5. Я долго думал над идеей и вспомнил, что летом я хотел создать свой язык, но у меня этого не получилось (Тогда я не понимал как работает парсер и абстрактное синтаксическое дерево, поэтому забросил). И вот, мне пришла идея - сделать свой язык программирования и написать для него IDLE (т.к. тема проекта все таки QT). Ещё полгода назад я изучал асинхронность и многопоточность, поэтому именно одну из этих идей я хотел воплотить в своём языке. В данной статье я хотел рассказать устройство интерпретируемых языков и как их создать.

Читать далее

Многопоточный Python на примерах: токены отмены

Уровень сложностиСложный
Время на прочтение10 мин
Количество просмотров12K

В этой статье мы обсудим паттерн "Cancellation Token", популярный в некоторых других языках, но почему-то обойденный вниманием в Python-сообществе. Он о том, как безопасно и красиво завершать работу функции, треда или корутины.

keep_on()

Здоровая конкуренция в GO. Главное не перехитрить самого себя

Уровень сложностиСредний
Время на прочтение15 мин
Количество просмотров16K

Несколько лет назад я прочитал статью о параллелизации в GO и ничего не понял – я тогда только начинал программировать на этом языке. Но размышления автора мне очень понравились – они подкреплялись бэнчмарками, что было довольно убедительно. Автор игрался c параметром GOMAXPROCS и показал, что увеличение этого параметра не всегда приводит к увеличению производительности. Под конец статьи он подобрал такое значение, которое будет максимально эффективным для его функции, на мое удивление, это значение оказалось равно единице! Т.е. его код работал максимально эффективно, если работал всего на одном ядре процессора! Однако, в одном из комментариев под той статьей я прочел, что все эти изыскания нелепы, поскольку та же самая функция из статьи запущенная всего в один поток оказывается эффективнее любой ее параллельной реализации.


С тех пор я написал уже много кода на GO, и могу поделиться мыслями о шаблонах параллельной обработки с теми, кто находится в том же состоянии, что и я когда то.



Читать дальше →

Многопоточность в Python: очевидное и невероятное

Уровень сложностиСредний
Время на прочтение4 мин
Количество просмотров43K

В данной статье я покажу на практическом примере как устроена многопоточность в Python, расскажу про потоки, примитивы синхронизации и о том зачем они нужны.

Изначально я планировал что это будет простая и короткая заметка, но пока готовил и тестировал код нашел интересный неочевидный момент связанных с внутренностями CPython, так что не спешите закрывать вкладку, даже если вы уверены что вы знаете о потоках в Python всё :)

Читать далее

Слышали ли вы про язык «e»? А ведь он был продан за $315 миллионов долларов

Время на прочтение10 мин
Количество просмотров37K

Все знают про язык программирования C, поменьше — про язык программирования F, кое‑кто про B, предшественник C, а вот знаете ли вы про язык «e»? Их кстати два — один с большой буквы «E», а другой с маленькой «e».

Вы наверное подумали, что это еще один безызвестный язык от какого‑нибудь аспиранта провинциального европейского университета. Однако интерпретатор маленького «e» под названием Specman продали в 2005 году большой компании Cadence Design Systems за $315 милионов долларов. Причем президента продающей компании Verisity звали Гаврилов. Также можно нагуглить, что этот язык использовали внутри компании Intel. Что же в нем такого, что вызвало интерес у толстых богатых корпораций?

Читать далее

Простейшая нейросеть: еще раз и подробнее

Время на прочтение10 мин
Количество просмотров64K

Машинное обучение это незаменимый инструмент для решения задач, которые легко решаются людьми, но не классическими программами. Ребенок легко поймет, что перед ним буква А, а не Д, однако программы без помощи машинного обучения справляются с этим весьма средне. И едва ли вообще справляются при минимальных помехах. Нейросети же уже сейчас решают многие задачи (включая эту) намного лучше людей. Их способность обучаться на примерах и выдавать верный результат поистине очаровывает, однако за ней лежит простая математика. Рассмотрим это на примере простого перцептрона.
Данная статья представляет собой пересказ-конспект первой части книги Тарика Рашида "Создай свою нейросеть" для тех, кто начал изучать тему, не понял отдельные детали или с трудом охватывает общую картину.

Читать далее

В условиях параллелизма обнуление памяти замедляется

Время на прочтение9 мин
Количество просмотров6.6K

Взявшись исследовать некоторые непонятки с производительностью в Chrome, я обнаружил, что Microsoft распараллелили обнуление памяти, и иногда работа из-за этого сильно замедляется. В Windows 11 такое замедление можно частично побороть, но в последних версиях Windows Server — где этот фактор наиболее важен — баг живее всех живых.

Но есть и хорошие новости: по-видимому, проблема актуальна только на тех машинах, где много процессоров. Под «много» я понимаю «96 или более». Поэтому вашего ноутбука проблема не касается. И даже 96-процессорные машины могут сталкиваться с ней не так часто. Но я нашел и другие факторы, из-за которых может быть спровоцирована такая неэффективность, если сложится подходящая ситуация — и обомлел. Вы бы посмотрели, как разбазаривается мощность ЦП. 

Окей – давайте перейдем к деталям.

Читать далее

Многопоточный Python на примерах: избавляемся от дедлоков

Время на прочтение8 мин
Количество просмотров18K

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

Разблокировать

Во что вам обойдется конкурентная обработка. Иерархия проблем

Время на прочтение47 мин
Количество просмотров6.3K

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

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

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

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

Читать далее

Почему мьютексы в Rust реализованы именно так

Время на прочтение17 мин
Количество просмотров14K

Я часто слышу от пробующих работать с Rust системных программистов жалобы на мьютексы и особенно на Rust Mutex API. Жалобы обычно выглядят так:

  • Они не хотят, чтобы мьютекс содержал данные, только блокировку.
  • Они не хотят управлять «защитным» значением, разблокирующим мьютекс при сбросе, в частности, они просто хотят вызывать операцию unlock, потому что им кажется, что это более явное действие.

Такие изменения превратили бы Rust mutex API в эквивалент C/Posix mutex API. Однажды я даже видел, как один разработчик пытался использовать Mutex<()> и разные хитрости, чтобы его имитировать.

Однако у такого стремления есть проблема: эти два аспекта Mutex неразрывно связаны друг с другом, а также с гарантиями безопасности Rust в целом — изменение одного из них или обоих откроет возможности для возникновения незаметных багов и повреждений из-за гонок данных.

Использование API мьютексов в стиле C, состоящего из набора косвенно защищаемых данных и из функций lock и unlock было бы опрометчивым в Rust, потому что это позволяет безопасному коду легко вносить ошибки, нарушающие безопасность памяти и вызывающие гонки данных.

Прозвучит спорно, но я утверждаю, что это справедливо и для C. Просто в Rust это более очевидно, поскольку Rust тщательно разделяет понятия «безопасного» кода, в который невозможно внести подобные ошибки, и «небезопасного» кода, в который можно вносить такие ошибки. В C такого разделения нет, и в результате этого использующий мьютексы код на C может тривиальным образом создавать серьёзные баги, которые потенциально можно подвергать эксплойтам.

В этом посте я разберу типичный C mutex API, сравню его с типичным Rust mutex API, и расскажу о том, что произойдёт, если мы изменим Rust API так, чтобы он напоминал C.
Читать дальше →

Баг в ядре Linux и как правильно жаловаться

Уровень сложностиСредний
Время на прочтение7 мин
Количество просмотров15K

Я работаю системным программистом в компании КриптоПро. Нередко мои задачи связаны с ошибками, которые лежат на самом нижнем уровне современных операционных систем, под которые мы пишем ПО. Я хочу поведать тебе, Хабр, об одной из таких ошибок и о том, как я жаловался на неё разработчикам.

Я отвечаю за поддержку одной из наших библиотек с C-интерфейсом, написанной на C и C++. Мой коллега из другого отдела сообщил, что его нагрузочный тест нашей библиотеки на C# в Linux выдаёт ошибку в хитром сценарии: нужно иметь два процесса по пять потоков, делающих некоторые идентичные вызовы. Если процесс один, а потоков много, то проблема не проявляется. Если процессов два, но в каждом по одному потоку, то проблема не проявляется. Путём просмотра исходников нагрузочного теста и логов работы библиотеки удалось перенести проблему в маленький юнит-тест на C++ с использованием нашего API.

Узнать, что же это было

Подключаем к Экселю GPU и ускоряем Эксель в 300 раз

Время на прочтение3 мин
Количество просмотров34K

Попалась мне задачка оптимизации, а так как я большой фанат Экселя, то и выбор инструмента был скорым. Единственная пакость: Эксель дико медленный. Так, на одну итерацию уходило как минимум 35 минут, а таких итераций планировалось сделать 1275 (как минимум)!

Цель этого небольшого проектика – ускорить исполнение VBA скриптов задействуя все доступные мне железяки: GPU и CPU. Ну и до кучи, так как библиотека моя, была реализована многозадачность.

О, да, я хочу на это посмотреть!

О Thread и ThreadPool в .NET подробно (часть 2)

Время на прочтение13 мин
Количество просмотров19K

В предыдущей публикации мы рассмотрели некоторые базовые вопросы относительно потоков и пулов потоков и готовы двигаться дальше. Давайте проведём эксперимент и найдём правильный объём работы для пула потоков. Чтобы его издержки не давлели над объёмом полезной работы

⚠️ Материал средней сложности

С другой стороны, показанные примеры доказывают, что на производительность сильно влияет гранулярность элементов работы. Имеется ввиду, конечно же, длительность работы делегатов. Чтобы достичь хороших показателей, гранулярность работы не может быть абы какой: она должна быть правильной. И помимо планирования задач на ThreadPool, планировать их можно также как через TPL так и через какой-либо свой собственный пул потоков. Например, если взять обычный ThreadPool, то можно примерно измерить издержки алгоритмов ThreadPool в тактах Time Stamp Counter счётчика времени (можно, конечно и в чём-то более привычном типа микросекунд, но там на многих сценариях вполне могут быть нули)

Читать далее

Ближайшие события

О Thread и ThreadPool в .NET подробно (часть 1)

Время на прочтение15 мин
Количество просмотров46K

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

А потому мы пройдёмся в первую очередь по IO-/CPU-bound операциям, стоимости создания потока, базовым основам работы пула потоков (но только основы), а далее -- углубимся в анализ чёрного ящика: от чего зависит производительность пула потоков? Каков объём работы приемлим для того чтобы в него планировать?

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

Также отмечу, что материал постепенно переходит от начального уровня сложности ? через ⚠️ средний уровень к ☠️ высокому, о чём вы сможете узнать по пиктограммам.

Погрузиться в знания

«Невозможный» параллельный алгоритм неотрицательной суммы

Время на прочтение4 мин
Количество просмотров9.6K

Рецепт параллельных вычислений Fork/Join или Map/Reduce:
- разбить задачу на куски;
- посчитать куски по-отдельности;
- склеить вместе.

Неотрицательная сумма (a, b) -> max(0, a + b) неассоциативна и результат зависит от порядка склейки. Она сломает Fork/Join и результат будет некорректен. Магией моноида починить на Java, SQL и Haskell за 5 минут, но

сломать мозг

Многопоточный Python на примерах: как правильно хранить настройки приложения

Время на прочтение14 мин
Количество просмотров36K

Если опустить первое и самое главное предубеждение относительно питонячьей многопоточности у большинства программистов — что её не существует из-за GIL, — то остается другое, и, наверное, вполне достоверное: что многопоточность — это сложно, и нам этого, пожалуйста, не надо. И знаете что? Так оно и есть. Многопоточность — это сложно, особенно когда выбираешься за пределы стандартных руководств и попадаешь со своей многопоточной поделкой в реальный мир. И, возможно, вам не нужно. Ни здесь, ни далее я не буду обсуждать целесообразность написания многопоточного кода на Python и сразу перейду к тому, как это делать.

Так как же?

В чём опасность слабой модели памяти ARM на примере конкретного эксплоита

Время на прочтение9 мин
Количество просмотров11K


Процессоры ARM приходят к нам всерьёз и надолго. Мы видим, что семейство Apple M1 в бенчмарках показывает потрясающие результаты, не хуже флагманских моделей от Intel и AMD, а кое-где и лучше их. Уже выпускаются 128-ядерные серверные ARM, которые ставят рекорды по энергоэффективности, а для серверов это очень важно.

Таким образом, ARM приходит и на десктопы, и на серверы. Но в разработке под архитектуру ARM и при работе с существующим программным обеспечением есть один нюанс. Дело в том, что программирование без блокировок (lock-free) — опасная штука, особенно на этих процессорах. Если на архитектуре x86/x64 сильная модель памяти и здесь инструкции типа store идут в процессор строго по порядку, то в архитектуре ARM это совершенно не факт. В результате частенько случается, что вполне безопасный код x86 порождает состояние гонки под ARM.
Читать дальше →

Многозадачность и многопоточность — распространенные заблуждения и недопонимания

Время на прочтение11 мин
Количество просмотров24K

Когда я предложил перевести на русский мою последнюю статью Easy Concurrency with Python Shared Objects на английском, поступило предложение "написать в несколько раз короче и понятнее". Просьба более чем обоснована. Поскольку я уже порядка десяти лет пишу многопоточку и БД, то описываемые мной логические связи выглядели самоочевидно, и я ошибочно расчитывал на аудиторию из трех с половиной человек, которые сидят сейчас где-то в яндексе или гугле. Судя по всему, они там и сидят, но тема им не интересна, поскольку в питоне нет настоящих потоков, а значит для этих людей такого языка программирования не существует. Потому я немножко снижаю планку и делаю общий обзор проблематики параллельных вычислений для людей, которые в них разбираются, но не являются экспертами в области.


Из-за чего весь сыр-бор?

Читать дальше →

Многопоточность на низком уровне

Время на прочтение14 мин
Количество просмотров40K

Очень часто при обсуждении многопоточности на платформе .NET говорят о таких вещах, как детали реализации механизма async/await, Task Asynchronous Pattern, deadlock, а также разбирают System.Threading. Все эти вещи можно назвать высокоуровневыми (относительно темы хабрапоста). Но что же происходит на уровне железа и ядра системы (в нашем случае — Windows Kernel)?


На конференции DotNext 2016 Moscow Гаэл Фретёр, основатель и главный инженер компании PostSharp, рассказал о том, как в .NET реализована многопоточность на уровне железа и взаимодействия с ядром операционной системы. Несмотря на то, что прошло уже пять лет, мы считаем, что никогда не поздно поделиться хардкорным докладом. Гаэл представил нам хорошую базу по работе процессора и атомнарным примитивам.



Вот репозиторий с примерами из доклада. А под катом — перевод доклада и видео. Далее повествование будет от лица спикера.

Project Loom: Современная маcштабируемая многопоточность для платформы Java

Время на прочтение27 мин
Количество просмотров27K


Эффективное использование многочисленных ядер современных процессоров — сложная, но всё более важная задача. Java была одним из первых языков программирования со встроенной поддержкой concurrency. Ее concurrency-модель, основанная на нативных тредах, хорошо масштабируется для тысяч параллельно выполняющихся стримов, но оказывается слишком тяжеловесной для современного реактивного программирования с сотнями тысяч параллельных потоков.


Ответ на эту проблему — Project Loom. Он определяет и реализует в Java новые легковесные параллельные примитивы.


Алан Бейтман, руководитель проекта OpenJDK Core Libraries Project, потратил большую часть последних лет на проектирование Loom таким образом, чтобы он естественно и органично вписывался в богатый набор существующих библиотек Java и парадигм программирования. Об этом он и рассказал на Joker 2020. Под катом — запись с английскими и русскими субтитрами и перевод его доклада.