все вопросы.
@cutwater Тегаю Вас, извините заранее.
Ваши ссылки прочитал.
Мне всетаки хочется разобраться в безобразии, которое творит питон с потоками. Что там вообще происходит в многопоточности, в частности.
Я создал небольшую песочницу для понимания скорости работы алгоритмов. Понятно, что функция map тут в лидерах, это не оспаривается. Интересуют немного другие приколы.
Код для поиграться здесь. https://pastebin.com/AjJCzmvS
simple_counter Correct 0.008378744125366211
simple_counter_for_gen Correct 0.010663986206054688
simple_counter_for Correct 0.014650106430053711
recursive_counter Correct 0.009507179260253906
recursive_counter_for Correct 0.009685039520263672 <- Вопрос здесь
full_recursive_iter Correct 0.05898594856262207
counter_threads Correct 0.015019893646240234
Вопрос 1: почему recursive_counter_for быстрее генератора simple_counter_for_gen и быстрее simple_counter_for, причем по второму не ожидал, что на столько.
Я знаю, что на мелких расчетах, производимых на месте в функции с небольшими входными данными, рекурсивные функции быстрее. Но тут даже с относительно большими данными без использования систем управления потоками, данные функции оказываются, хоть и незначительно, но быстрее.
Есть объяснения происходящему в питоне как со стороны GIL, так и с других сторон?
PS: Я не настоящий сварщик программист на питоне. Я немного из другой области выпал, поэтому до сих пор считаю себя новичком.
питон 3.11.1 x64
а вопросы "почему быстрее" учитывают разницу в версиях питона?
питон 3.11.1 x64
Так, ну до того как можно будет что-то обсуждать про скорость, выкинь time.time для замеров и возьми что-нибудь, считаютщее не погоду на марсе. perf_counter, например. А лучше возьми timeit чтобы считало не один раз а несколько. Всё-таки у нас ОС многозадачная.
тоже самое получается, я думаю о профилировании вообще, чтобы понять почему так. Это песочница, по ссылке выше, тоже можете поиграться и подкинуть перфкоунтер.
я вообще за идеи, почему может быть так, и где тут собака порылась, что не смотря на все дополнительные накладные расходы в вызовах функций полно непоняток по последоввательному исполнению кода.
Ну, я вижу просто вывод про то что map всех победил и подозреваю, что что-то должно быть не так с методикой подсчёта. В сотый раз проверять одно и то же мне уже как-то не очень интересно, но погадать на адекватно полученных результатах можно бы.
Я, как это ни странно, за гадания, как раз. Реально интересно почему происходит именно так, что последовательный вызов со слайсами, оказывается, в среднем, быстрее. За map, я не переживаю, он будет в лидерах именно при этой реализации, это функция C, ее не догнать из питона никак.
Функция на Си не показатель. Она может быть плохо написана (чет душню, и в целом хуйню написал)
Ну про map, вопросов нет, там все очень хорошо. Тут вопросы в связке с GIL образуются.
я ставлю на некорректные замеры
Я пока, что называется, not convinced, что функции делают одно и то же. Возможно, что мне станет достаточно скучно, что под кофе почитаю и сравню. :-)
Я код выложил. Я займусь техниками изучения производительности функций в питоне, так же как и профилированием, но на это уйдёт время, которого не так много. Если у Вас есть возможность, то код можно подпилить, под корректные для Вас измерения. Я тут в роли просящего и настаивать ни на чем не имею права. Мне просто нужны идеи, почему так происходит. Идея 1: некорректные метрики производительности.
А можно как-то сформулировать, в чём была идея этих рекурсивных функций? Типа просто нарубить задачу на подзадачи и деревом посчитать? Ну и да, что за global div и нафига оно так при том, что это по сути своей константа?
И да, так задумано? def recursive_counter_for(s, r=False): ... ret += recursive_counter(s[j:i], True) То есть оно какое-то не сильно рекурсивное.
Идея в том, что иногда, для жеской оптимизации вычислений, я не использую многопоточность в ручном виде, а именно использую подход, нарубить по кусочкам и скормить функции, рекурсивно или нет, это вопрос вторичный. Мое понимание работы стека, а каком то месте, видимо не правильно. Я задался вопросом, почему скорость работы некоторых функций не совпадает с пониманием большинства в "затыках с GIL". Далее я решил просто написать тест, и посмотреть, а что будет, если я задачу обработки данных попробую решить разными способами, которые я знаю. Ну и уперся в вопрос, WTF? (ЧЗНХ?), что за странная оптимизация байткода и ускорение, где его, по идее, быть не должно.
Абсолютно верно, это не рекурсия, а просто использование последовательного вызова функции впринципе. Чтобы не захламлять пространство имен, сделано так.
В смысле "не захламлять пространство имён"? Не понял.
Чистая рекурсия там тоже есть. Но в данном примере, т.к. количество данных велико, она будет неэффективна. Рекурсии эффективны в расчетах внутри, при малом входящем размере данных и малом исходящем. Иначе можно столкнуться с ожиданием аллокатора и как раз выпасть на GIL.
Ок, идею я примерно понял, сомневаюсь, что это как-то связано со стеком, но в целом стало немного любопытно и можно поэкспериментировать чуть позже.
Ну, я подумал, что "не захламлять" было про div и на всякий случай удивился.
А, нет. Писать глобал это привычка. Сразу видно, что внутри функции или класса я использую что-то внешнее, без написания длинных докстрингов и не раздувая параметры.
Ну, это скорее наоборот, путь к проблемам, чем что-то хорошее. :-D
Это уровень: "Все трюки сделаны профессионалами, не повторяйте это дома в проде." Много параметров глобала использовать - это вообще ужасная практика. Но использовать константу внешнего мира внутри функции, почему бы и нет?
Не, у тебя тут просто типичная константа и заглавными буквами без всякого global смотрелась бы прекрасно. А так у тебя есть блестящая возможность опечаткой поменять глобальную недоконстанту и потом долго об этом не узнать.
Кстати, вопрос по пути. У меня есть .env и всякие конфиги, я из них все загружаю в переменные глобальные. Глобалы могут быть изменены на ходу, тоесть в конфигах стартовые параметры. Это как практичней делать? Именно от туда у меня пошла привычка глобалы делать.
все загружаю в переменные глобальны 😢
Ой, это, видимо, сюда: https://t.me/advice17/6
особо в код не вникал, но после замены time.time на time.perfcounter результаты соответствуют ожиданиям
Это немного не мой прикол. Я из настроек тащу стартовые параметры, которые изменяются по ходу исполнения кода. В Глобал я ток такое засовываю. Кстати, так и было в коде, ссылку на который я давал выше, в стартовом сообщении. Я менял переменную div, для того, чтобы понять, где какая функция начнёт лидировать на каком количестве потоков. В код я это не включил.
Ну, примерно так делает джанга со своими настройками. Для чего-то в продакшене — плохая идея как раз по тем причинам, что описаны в посте. Для всяких исследовательских экспедиций, где заморачиваться с нормальным настройками смысла мало — ок. Но это по сути всё ещё глобальные константы, просто с некой отложенной инициализацией, так что нормально их оставлять тупо заглавными. Ключевое слово global тут сбивает с толку и делать так точно не стоит.
Учту, спасибо. Буду понимать, что нормальная практика, именно, описанная Вами.
Ну там рассказали про солид и про то, как НЕ делать. А как делать то?
Обсуждают сегодня