Будет ли прирост производительности при использовании потоков ОС для вычислений и почему
Что проверяют
Знаете ли вы про GIL и понимаете ли, что он ограничивает не потоки ОС, а выполнение байт-кода. Следом обычно спрашивают, чем тогда распараллелить расчёт и слышали ли вы про сборку Python без GIL.
Как отвечать
Потоки из threading в CPython это настоящие потоки ОС, система раскидывает их по ядрам. Но в стандартной сборке поток должен держать GIL, чтобы выполнять байт-код, поэтому Python-код в каждый момент исполняет только один из них.
- чистый Python-расчёт: прироста нет. Бывает даже медленнее, чем в одном потоке, из-за борьбы за GIL и переключений (интервал задаёт
sys.setswitchinterval(), по умолчанию 5 мс); - ввод-вывод: на блокирующем вызове (сокет, файл,
time.sleep) поток отпускает GIL, поэтому для сетевых запросов потоки работают хорошо; - C-расширения: NumPy,
hashlib,zlibи многие другие отпускают GIL на тяжёлых операциях, и такая математика в потоках масштабируется по ядрам; - процессы:
multiprocessingилиProcessPoolExecutor. У каждого процесса свой интерпретатор и свой GIL, плата это запуск процессов и сериализация данных черезpickle; - free-threaded CPython: сборка без GIL (PEP 703) появилась экспериментально в 3.13, в 3.14 стала официально поддерживаемой, но ставится отдельно (
python3.14t) и по умолчанию не используется. В ней потоки дают прирост на расчётах, если ваши C-расширения совместимы с этим режимом.
Закончите правилом выбора: задачам с ожиданием подходят потоки или asyncio, вычислениям процессы, векторизация в NumPy или вынос горячего кода в C, Cython, Rust.
Где спотыкаются
Говорят, что в Python нет настоящих потоков. Они есть, ограничение накладывает GIL, а не отсутствие потоков ОС.
Утверждают, что GIL делает код потокобезопасным. x += 1 состоит из нескольких инструкций байт-кода, поток могут прервать между ними, так что гонки и Lock никуда не деваются.
Советуют asyncio для ускорения вычислений. Event loop работает в одном потоке и помогает только там, где много ожидания.