OPENSOURCE_FINDINGS Telegram 916
PEP-734: Subinterpreters in stdlib

- PEP: https://peps.python.org/pep-0734
- Обсуждение: https://discuss.python.org/t/pep-734-multiple-interpreters-in-the-stdlib/41147
- Документация: https://docs.python.org/3.14/library/concurrent.interpreters.html

Что оно такое?

Несколько полноценных интерпретаторов работающих рядом. Какие плюсы?
- Один процесс
- Один тред, но руками можно создавать еще
- Простые данные можно шарить без необходимости pickle, сложные нужно пиклить
- По GILу на интерпретатор, все еще можно получить плюшки настоящей многозадачности по сети
- Работает с asyncio

Минусы:
- C код нужно было значительно переработать, не все C расширения поддерживаются (пока)

Получается хорошая универсальность для разных задач.

Немного истории

Есть несколько важных нетехнических аспектов про процесс создания данной фичи:
- PEP-734 и Free-Threading делают очень похожие вещи – позволяют реализовывать настоящую многозадачность, но разными способами
- Изначально субинтерпретаторы появились в 3.10 в виде только C-шного АПИ
- Есть отдельный PyPI пакет с данным кодом
- Пайтон часть в виде PEP-734 был добавлена в 3.14 уже после feature freeze
- Изначально планировалось добавить его как модуль interpreters, однако в последний момент он стал concurrent.interpreters, вот тут доступно большое обсуждение

Как работает?

Внутри довольно много разных C-шных модулей:
- Основа: https://github.com/python/cpython/blob/main/Python/crossinterp.c
- Дефиниция модуля: https://github.com/python/cpython/blob/main/Modules/_interpretersmodule.c
- Очередь для обмена сообщениями между интерпретаторами: https://github.com/python/cpython/blob/main/Modules/_interpqueuesmodule.c
- Набор примитивов: https://github.com/python/cpython/blob/main/Modules/_interpchannelsmodule.c

Но, для пользователей - важен только питоновский АПИ, что прекрасно. Он получился простым и понятным:


interp = interpreters.create()
try:
interp.exec('print("Hello from PEP-554")')
finally:
interp.close()


Давайте посмотрим на пример и замерим!

Тестирую на M2Pro 2023. Полный код тут.
Код, CPU-bound задача, считаем факториалы от числа до числа:


def worker_cpu(arg: tuple[int, int]):
start, end = arg
fact = 1
for i in range(start, end + 1):
fact *= i


Будем разбивать работу на 4 части, считать факториал первых 10000, потом вторых и тд. Вот так запускаем сабинтерпретаторы:


from concurrent.futures import InterpreterPoolExecutor

def bench_subinterpreters():
with InterpreterPoolExecutor(CPUS) as executor:
list(executor.map(worker, WORKLOADS))


Аналогично запускаем и треды с процессами.

Результаты:


Regular: Mean +- std dev: 163 ms +- 1 ms
Threading with GIL: Mean +- std dev: 168 ms +- 2 ms
Threading NoGIL: Mean +- std dev: 48.7 ms +- 0.6 ms
Multiprocessing: Mean +- std dev: 73.4 ms +- 1.5 ms
Subinterpreters: Mean +- std dev: 44.8 ms +- 0.5 ms


Субинтерпретаторы значительно ускорили данный пример. Работает даже быстрее FT 😱

А теперь для IO-bound задачи возьмем такой пример:


def worker_io(arg: tuple[int, int]):
start, end = arg
with httpx.Client() as client:
for i in range(start, end + 1):
client.get(f'http://jsonplaceholder.typicode.com/posts/{i}')


И снова concurrent.interpreters показывают хорошее время:


Regular: Mean +- std dev: 1.45 sec +- 0.03 sec
Threading with GIL: Mean +- std dev: 384 ms +- 17 ms (~1/4 от 1.45s)
Threading NoGIL: Mean +- std dev: 373 ms +- 20 ms
Multiprocessing: Mean +- std dev: 687 ms +- 32 ms
Subinterpreters: Mean +- std dev: 547 ms +- 13 ms


Тут может показаться, что как-то не очень много перформанса у нас получилось. Но! Вспоминаем, что внутри можно создавать дополнительные треды, чтобы еще ускорить работу. А можно даже и asyncio так параллелить, хотя я пока и не пробовал.

Обсуждение: как вам фича?

| Поддержать | YouTube | GitHub | Чат |
53👍7821🔥13🤡4🤔2



tgoop.com/opensource_findings/916
Create:
Last Update:

PEP-734: Subinterpreters in stdlib

- PEP: https://peps.python.org/pep-0734
- Обсуждение: https://discuss.python.org/t/pep-734-multiple-interpreters-in-the-stdlib/41147
- Документация: https://docs.python.org/3.14/library/concurrent.interpreters.html

Что оно такое?

Несколько полноценных интерпретаторов работающих рядом. Какие плюсы?
- Один процесс
- Один тред, но руками можно создавать еще
- Простые данные можно шарить без необходимости pickle, сложные нужно пиклить
- По GILу на интерпретатор, все еще можно получить плюшки настоящей многозадачности по сети
- Работает с asyncio

Минусы:
- C код нужно было значительно переработать, не все C расширения поддерживаются (пока)

Получается хорошая универсальность для разных задач.

Немного истории

Есть несколько важных нетехнических аспектов про процесс создания данной фичи:
- PEP-734 и Free-Threading делают очень похожие вещи – позволяют реализовывать настоящую многозадачность, но разными способами
- Изначально субинтерпретаторы появились в 3.10 в виде только C-шного АПИ
- Есть отдельный PyPI пакет с данным кодом
- Пайтон часть в виде PEP-734 был добавлена в 3.14 уже после feature freeze
- Изначально планировалось добавить его как модуль interpreters, однако в последний момент он стал concurrent.interpreters, вот тут доступно большое обсуждение

Как работает?

Внутри довольно много разных C-шных модулей:
- Основа: https://github.com/python/cpython/blob/main/Python/crossinterp.c
- Дефиниция модуля: https://github.com/python/cpython/blob/main/Modules/_interpretersmodule.c
- Очередь для обмена сообщениями между интерпретаторами: https://github.com/python/cpython/blob/main/Modules/_interpqueuesmodule.c
- Набор примитивов: https://github.com/python/cpython/blob/main/Modules/_interpchannelsmodule.c

Но, для пользователей - важен только питоновский АПИ, что прекрасно. Он получился простым и понятным:


interp = interpreters.create()
try:
interp.exec('print("Hello from PEP-554")')
finally:
interp.close()


Давайте посмотрим на пример и замерим!

Тестирую на M2Pro 2023. Полный код тут.
Код, CPU-bound задача, считаем факториалы от числа до числа:


def worker_cpu(arg: tuple[int, int]):
start, end = arg
fact = 1
for i in range(start, end + 1):
fact *= i


Будем разбивать работу на 4 части, считать факториал первых 10000, потом вторых и тд. Вот так запускаем сабинтерпретаторы:


from concurrent.futures import InterpreterPoolExecutor

def bench_subinterpreters():
with InterpreterPoolExecutor(CPUS) as executor:
list(executor.map(worker, WORKLOADS))


Аналогично запускаем и треды с процессами.

Результаты:


Regular: Mean +- std dev: 163 ms +- 1 ms
Threading with GIL: Mean +- std dev: 168 ms +- 2 ms
Threading NoGIL: Mean +- std dev: 48.7 ms +- 0.6 ms
Multiprocessing: Mean +- std dev: 73.4 ms +- 1.5 ms
Subinterpreters: Mean +- std dev: 44.8 ms +- 0.5 ms


Субинтерпретаторы значительно ускорили данный пример. Работает даже быстрее FT 😱

А теперь для IO-bound задачи возьмем такой пример:


def worker_io(arg: tuple[int, int]):
start, end = arg
with httpx.Client() as client:
for i in range(start, end + 1):
client.get(f'http://jsonplaceholder.typicode.com/posts/{i}')


И снова concurrent.interpreters показывают хорошее время:


Regular: Mean +- std dev: 1.45 sec +- 0.03 sec
Threading with GIL: Mean +- std dev: 384 ms +- 17 ms (~1/4 от 1.45s)
Threading NoGIL: Mean +- std dev: 373 ms +- 20 ms
Multiprocessing: Mean +- std dev: 687 ms +- 32 ms
Subinterpreters: Mean +- std dev: 547 ms +- 13 ms


Тут может показаться, что как-то не очень много перформанса у нас получилось. Но! Вспоминаем, что внутри можно создавать дополнительные треды, чтобы еще ускорить работу. А можно даже и asyncio так параллелить, хотя я пока и не пробовал.

Обсуждение: как вам фича?

| Поддержать | YouTube | GitHub | Чат |

BY Находки в опенсорсе




Share with your friend now:
tgoop.com/opensource_findings/916

View MORE
Open in Telegram


Telegram News

Date: |

The group also hosted discussions on committing arson, Judge Hui said, including setting roadblocks on fire, hurling petrol bombs at police stations and teaching people to make such weapons. The conversation linked to arson went on for two to three months, Hui said. Each account can create up to 10 public channels Healing through screaming therapy Co-founder of NFT renting protocol Rentable World emiliano.eth shared the group Tuesday morning on Twitter, calling out the "degenerate" community, or crypto obsessives that engage in high-risk trading. A new window will come up. Enter your channel name and bio. (See the character limits above.) Click “Create.”
from us


Telegram Находки в опенсорсе
FROM American