Не очень понятно, как уменьшение размера на 0.06% так сильно влияет на скорость загрузки. У вас условно была библиотека 300Мб и осталась примерно столько же. Так все таки - за счет чего такой прирост производительности?
Есть важный нюанс: планировщик операционной системы сам решает, когда выделить потокам время на выполнение. При этом GIL в CPython заставляет поток освобождать его примерно раз в 5 миллисекунд.
* For the longest time, the eval breaker check would happen
* frequently, every 5 or so times through the loop, regardless
* of what instruction ran last or what would run next. Then, in
* early 2021 (gh-18334, commit 4958f5d), we switched to checking
* the eval breaker less frequently, by hard-coding the check to
* specific places in the eval loop (e.g. certain instructions).
* The intent then was to check after returning from calls
* and on the back edges of loops.
*
* In addition to being more efficient, that approach keeps
* the eval loop from running arbitrary code between instructions
* that don't handle that well. (See gh-74174.)
*
* Currently, the eval breaker check happens on back edges in
* the control flow graph, which pretty much applies to all loops,
* and most calls.
* (See bytecodes.c for exact information.)
В плане реквестов в CPython довольно просто - это открытый проект, с открытой политикой, в массе своей поддерживается и развивается волонтерами, есть краткий гайд - Python Developer’s Guide .
В этом плане любой может прийти и создать реквест. Только чтобы изменения попали в "мастер" ветку, нужно чтобы реквест прошел ревью и одобрение со стороны code-owners. В моем случае Нил забраковал решение и сделал свое. Нил если что занимался GC в питоне еще в начале 00, так что тут никаких обид :)
Чтобы разобраться - нужно просто сидеть и разбираться. Здесь тоже нет ничего необычного - нужно желание и время. Ну и мне в определенной степени повезло, что проблема была связана с той подсистемой с которой я разбирался ранее.
Основной способ — использовать C-расширения, но такой вариант подходит не всем, поэтому в небольших проектах часто прибегают к процессам и асинхронности.
В мире принятого PEP734 (subinterpreters) не рассказать о них - преступление!
Все таки наверное 3.3 если вы упоминаете это видео в разделе про 3.3.
Условие if — внутри if-а находится проверка флага eval_breaker. Если он установлен в true, поток прекращает выполнение байт-кода и освобождает GIL, об этом поговорим ниже.
Это давно уже не так. eval_breaker содержит различные биты, которые управляют разными функциями.
Теоретически проблему можно решить, добавив отдельную блокировку ко всем структурам данных, которые могут использоваться разными потоками. Но это решение неэффективно, так как есть риск взаимоблокировок — ситуации, когда два или более потока ждут друг друга, захватив разные ресурсы, и ни один из них не может продолжить работу.
Так весь PEP703 (free-threading build) как раз про это.
По истечении 5 мс устанавливается флаг gil_drop_request = 1, который заставляет первый поток остановиться. Второй поток снова вызывает cv_wait().
Упоминания gil_drop_requestостались только в комментариях.
itertools.cycle(iterable) — возвращает по одному значению из последовательности, повторенной бесконечное количество раз, то есть зацикленной. На вход данная функция принимает любое итерируемое значение.
У вас тут не правильное описание.
@lru_cache(maxsize=128, typed=False) — это декоратор, который кэширует данные функции. Такое может пригодиться, чтобы сэкономить время при очень дорогих вычислениях, если вы вызываете функцию с одними и теми же аргументами
это декоратор, который кэширует данные функции - так говорить не совсем корректно, особенно в условиях когда у нас функции высшего порядка.
Понятно, что JIT в той же Java не ограничен специализаций. И специализация это был (насколько я понимаю) один из шагов в направлении ускорения вычислений в CPython, без радикального изменения интерпретатора. В faster-cpython/ideas вроде был описан переход от специализации к JIT. Но в CPython катастрофически не хватает а) рук, б) понимающих людей. Сейчас над ним работают, наверное, не больше 5 человек. А с учетом того, что MS фактически прикрыл проект по оптимизации, то в целом все довольно грустно.
Если есть желание и возможности поучаствовать в развитии JIT в питоне - то все возможно. Можно также присоединиться к чату https://t.me/opensource_findings_chat - пара CPython Core dev там обитает.
Вы задаете правильный вопрос - зачем. Самый простой ответ выполнить очистку/удаление/финализацию (называйте как хотите) объекта. Т.к. бессмертные объекты сейчас в основном внутренние системные объекты, то мы можем знать когда на эти объекты не осталось ссылок. В этом случае мы можем выполнить `_Py_SetMortal(op, 1)` и после этого `Py_DECREF(op)` чтобы очистить объект. Понятно, что это не относится к accidentally immortal объектам - они у нас просто утекут в данный момент. Думаю что PEP 797 в каком-то виде ответит на эти вопросы, стоит немного подождать.
Я не ставлю под сомнение вашу честность :)
Просто совершенно не очевидно стоит ли игра свеч и масштабируется ли ваш опыт за пределы вашего проекта.
Не очень понятно, как уменьшение размера на 0.06% так сильно влияет на скорость загрузки. У вас условно была библиотека 300Мб и осталась примерно столько же. Так все таки - за счет чего такой прирост производительности?
Это напоминание о моей глупости - я думал я понимаю как работает GIL, а оказалось не понимаю. Будет повод разобраться глубже.
Спасибо!
Спасибо!
Спасибо организаторам за крутую конфу, а коллегам за очень интересные доклады! Узнал много нового!
Спасибо, было интересно!
Хотел бы добавить комментарий - не стоит забывать, что в случае
gc.set_debug(gc.DEBUG_LEAK)
весь мусор уедет вgc.garbage
и там и останется.Презентация от автора про
tail-calling interpreter
https://docs.google.com/presentation/d/10oznjsxU45TzdwCrtLoK9cm3xcsavIYh2vlZ2RkWkhU/edit?slide=id.g344929f9bd2_1_6#slide=id.g344929f9bd2_1_6В массе своей все равно энтузиастами. Это вот такая объективная реальность данная нам в ощущениях.
Ну я мастер формулировок, простите если запутал.
В плане реквестов в CPython довольно просто - это открытый проект, с открытой политикой, в массе своей поддерживается и развивается волонтерами, есть краткий гайд - Python Developer’s Guide .
В этом плане любой может прийти и создать реквест. Только чтобы изменения попали в "мастер" ветку, нужно чтобы реквест прошел ревью и одобрение со стороны code-owners. В моем случае Нил забраковал решение и сделал свое. Нил если что занимался GC в питоне еще в начале 00, так что тут никаких обид :)
Чтобы разобраться - нужно просто сидеть и разбираться. Здесь тоже нет ничего необычного - нужно желание и время. Ну и мне в определенной степени повезло, что проблема была связана с той подсистемой с которой я разбирался ранее.
После такого наброса вряд ли получится конструктивная беседа. Вот реквест, который не был принят, потому что добавлял еще один проход в сборщик мусора - gh-135552: Clear weakrefs to types in GC after garbage finalization not before by sergey-miryanov · Pull Request #135728 · python/cpython
Да, все так. CPython большой и сложный проект, с большим количеством нетривиальных связей, которые не документированы.
Del
Спасибо!
В мире принятого PEP734 (subinterpreters) не рассказать о них - преступление!
Все таки наверное 3.3 если вы упоминаете это видео в разделе про 3.3.
Это давно уже не так.
eval_breaker
содержит различные биты, которые управляют разными функциями.Так весь PEP703 (free-threading build) как раз про это.
Упоминания
gil_drop_request
остались только в комментариях.В целом не понятно о какой версии вы говорите.
За попытку плюс, за остальное - так себе.
itertools.cycle(iterable) — возвращает по одному значению из последовательности, повторенной бесконечное количество раз, то есть зацикленной. На вход данная функция принимает любое итерируемое значение.
У вас тут не правильное описание.
@lru_cache(maxsize=128, typed=False) — это декоратор, который кэширует данные функции. Такое может пригодиться, чтобы сэкономить время при очень дорогих вычислениях, если вы вызываете функцию с одними и теми же аргументами
это декоратор, который кэширует данные функции - так говорить не совсем корректно, особенно в условиях когда у нас функции высшего порядка.
Остальное простите не смотрел.
Тем более присоединяйтесь, будете разваливать его изнутри, так сказать :)
Понятно, что JIT в той же Java не ограничен специализаций. И специализация это был (насколько я понимаю) один из шагов в направлении ускорения вычислений в CPython, без радикального изменения интерпретатора. В faster-cpython/ideas вроде был описан переход от специализации к JIT. Но в CPython катастрофически не хватает а) рук, б) понимающих людей. Сейчас над ним работают, наверное, не больше 5 человек. А с учетом того, что MS фактически прикрыл проект по оптимизации, то в целом все довольно грустно.
Если есть желание и возможности поучаствовать в развитии JIT в питоне - то все возможно. Можно также присоединиться к чату https://t.me/opensource_findings_chat - пара CPython Core dev там обитает.
Насколько я понимаю, аналог этого описан в PEP 659 – Specializing Adaptive Interpreter | peps.python.org и реализован с 3.11. В терминах CPython это еще не JIT, работы по JIT-у только ведутся.
Вы задаете правильный вопрос - зачем. Самый простой ответ выполнить очистку/удаление/финализацию (называйте как хотите) объекта. Т.к. бессмертные объекты сейчас в основном внутренние системные объекты, то мы можем знать когда на эти объекты не осталось ссылок. В этом случае мы можем выполнить `_Py_SetMortal(op, 1)` и после этого `Py_DECREF(op)` чтобы очистить объект. Понятно, что это не относится к accidentally immortal объектам - они у нас просто утекут в данный момент. Думаю что PEP 797 в каком-то виде ответит на эти вопросы, стоит немного подождать.