Kangaroo
2.28K subscribers
112 photos
61 files
432 links
Kangaroo — о программировании и технологиях.

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

Иногда — математика и котики :)

По всем вопросам: @Lodhel
Download Telegram
Global Interpreter Lock (GIL) — это способ синхронизации потоков, который используется в некоторых интерпретируемых языках программирования, например в Python и Ruby.

GIL является самым простым способом избежать конфликтов при одновременном обращении разных потоков к одним и тем же участкам памяти. Когда один поток захватывает его, GIL, работая по принципу мьютекса, блокирует остальные. Нет параллельных потоков — нет конфликтов при обращении к разделяемым объектам. Очерёдность выполнения потоков определяет интерпретатор в зависимости от реализации, переключение между потоками может происходить: когда активный поток пытается осуществить ввод-вывод, по исчерпании лимита выполненных инструкций либо по таймеру.

Главный недостаток подхода обеспечения потокобезопасности при помощи GIL — это ограничение параллельности вычислений. GIL не позволяет достигать наибольшей эффективности вычислений при работе на многоядерных и мультипроцессорных системах. Также использование нескольких потоков накладывает издержки на их переключение из-за эффекта конкуренции (потоки «пытаются» перехватить GIL). То есть многопоточное выполнение может занять большее время, чем последовательное выполнение тех же задач.

Причины использования GIL:
Однопоточные сценарии выполняются значительно быстрее, чем при использовании других подходов обеспечения потокобезопасности;
Простая интеграция библиотек на C, которые зачастую тоже не потокобезопасны;
Простота реализации.

GIL используется в CPython'е, наиболее распространённой реализации интерпретатора языка Python , и в Ruby MRI, эталонной реализации интерпретатора языка Ruby, где он зовётся Global VM Lock.

В сети не раз появлялись петиции и открытые письма с просьбой убрать GIL из Python'а. Однако создатель и «великодушный пожизненный диктатор» проекта Гвидо ван Россум заявляет, что GIL не так уж и плох и он будет в CPython'е до тех пор, пока кто-то другой не представит реализацию Python'а без GIL, с которой бы однопоточные скрипты работали так же быстро.

Реализации интерпретаторов на JVM (Jython, JRuby) и на .NET (IronPython, IronRuby) не используют GIL.

В рамках проекта PyPy ведётся работа по реализации транзакционной памяти (англ. Software Transactional Memory, SТМ). На данный момент даже в многопоточных вычислениях интерпретатор с STM работает во много раз медленней, чем с GIL. Но за счёт JIT PyPy-STM всё равно быстрее, чем CPython.
В Python есть одна проблема: есть глобальная блокировка интерпретатора, которая не позволяет двум потокам в одном и том же процессе одновременно запускать код Python. Это означает, что если у вас 8 ядер и вы измените код на использование 8 потоков, он не сможет использовать 800% ЦП и работать в 8 раз быстрее; он будет использовать тот же 100% процессор и работать с той же скоростью. (На самом деле, он будет работать немного медленнее, потому что при многопоточности возникают дополнительные издержки, даже если у вас нет общих данных, но пока игнорируйте это.)

Есть исключения из этого. Если тяжелые вычисления в вашем коде на самом деле не происходят в Python, но в какой-то библиотеке с пользовательским кодом C, которая выполняет правильную обработку GIL, например, в numpy-приложении, вы получите ожидаемый выигрыш в производительности от потоков. То же самое верно, если тяжелые вычисления выполняются каким-то подпроцессом, который вы запускаете и ждете.

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

Но если вы пишете программу с привязкой к процессору на чистом Python, использование большего количества потоков обычно не помогает.

Использование отдельных процессов не имеет таких проблем с GIL, потому что каждый процесс имеет свой отдельный GIL. Конечно, между потоками и процессами у вас все еще есть те же компромиссы, что и в любых других языках - разделять данные между процессами труднее и дороже, чем между потоками, может быть дорого запускать огромное количество процессов или создавать и уничтожать их часто и т. д. Но GIL сильно влияет на баланс между процессами, и это не так, скажем, для C или Java. Таким образом, вы обнаружите, что используете многопроцессорность гораздо чаще в Python, чем в C или Java.

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

Если вы разрабатываете свой код в терминах автономных «заданий», которые ничего не делят с другими заданиями (или основной программой), кроме ввода и вывода, вы можете использовать concurrent.futures библиотеку для написания кода вокруг пула потоков, например:

with
concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor: executor.submit(job, argument) executor.map(some_function, collection_of_independent_things)
# ...


Вы даже можете получить результаты этих заданий и передать их на дальнейшие задания, ждать, пока все будет в порядке выполнения или в порядке завершения и т. Д .; прочитайте раздел об Futureобъектах для деталей.

Теперь, если окажется, что ваша программа постоянно использует 100% ЦП, а добавление большего количества потоков просто замедляет ее, то вы столкнулись с проблемой GIL, поэтому вам нужно переключиться на процессы. Все, что вам нужно сделать, это изменить эту первую строку:
with concurrent.futures.ProcessPoolExecutor(max_workers=4) as executor

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

Но что, если ваша работа не может быть самостоятельной? 
Если вы можете разработать свой код с точки зрения заданий, которые передают сообщения от одного к другому, это все еще довольно легко. Возможно, вам придется использовать threading.Threadили multiprocessing.Processвместо того, чтобы полагаться на пулы. И вам придется создавать объекты queue.Queueили multiprocessing.Queueявно. (Существует множество других опций - каналы, сокеты, файлы со скоплениями, ... но дело в том, что вы должны сделать что-то вручную, если автоматическая магия исполнителя недостаточна.)

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

https://habr.com/ru/post/480220/
В чем разница между потоком и процессом?

Процессы и потоки связаны друг с другом, но при этом имеют существенные различия.

Процесс — экземпляр программы во время выполнения, независимый объект, которому выделены системные ресурсы (например, процессорное время и память). Каждый процесс выполняется в отдельном адресном пространстве: один процесс не может получить доступ к переменным и структурам данных другого. Если процесс хочет получить доступ к чужим ресурсам, необходимо использовать межпроцессное взаимодействие. Это могут быть конвейеры, файлы, каналы связи между компьютерами и многое другое.

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

Поток — определенный способ выполнения процесса. Когда один поток изменяет ресурс процесса, это изменение сразу же становится видно другим потокам этого процесса.
Гид по языку python ⤵️
В Китае стартуют продажи карт Super SIM

Эра сетей и смартфонов 5G принесла с собой «побочный» продукт в виде так называемой карты Super SIM. Как пишет источник, ее продажи в Китае стартуют уже 23 декабря.

За разработкой Super SIM стоит местная компания Ziguang, а самое главное ее отличие от обычной SIM – наличие 128 ГБ встроенной флэш-памяти. Применение такой SIM-карты позволит расширить память даже той модели, которая не поддерживает карты памяти microSD. Отмечается, что на Super SIM пользователи могут осуществлять резервное копирование данных или попросту копировать фото или видео – в любом случае данные будут надежно защищены.

128 ГБ – это максимальный объем флэш-памяти Super SIM, пользователям предложат варианты попроще – с 32 и 64 ГБ памяти. А в будущем объем флэш-памяти Super SIM увеличится до 512 ГБ и даже 1 ТБ.
Представляем Numba: высокопроизводительный компилятор Python


https://morioh.com/p/ea62e7fb53da?f=5c21fb01c16e2556b555ab32