Дизассемблируем Python-циклы. Ассимиляция Гиперпространственного Фокала Социалистических Измерений!
Мой курс «Хардкорная веб-разработка» - course.to.digital
Книжный клуб Ботаним!, где мы читаем хорошие ИТ-книги: botanim.to.digital/
Telegram - t.me/t0digital
Препарируем Python-циклы по полной!
0:00 Стартуем
0:31 О чём речь
1:14 Всё дело в обходе структуры!
8:24 Надо просто вынести range за функции!
10:37 Что такое итерация?
19:39 Упрощаем код
20:09 Адовые тесты!
23:13 А если переписать на while?
24:11 while работает медленнее, чем for?
26:12 Выводы
Первое видео - • Ускоряем вложенные цик...
/****************** about ******************/
Меня зовут Алексей Голобурдин, я программирую с 2004 года и на этом канале делюсь своим опытом. Я основатель и руководитель компаний:
- Диджитализируй digitalize.team, разрабатываем сложные IT системы для бизнеса;
- Salesbeat salesbeat.pro, комплексный модуль доставки для интернет магазинов.
Telegram канал - t.me/t0digital
ВК - digitalize.team
RuTube - rutube.ru/channel/24802975/ab...
Дзен - dzen.ru/id/6235d32cb64df01e6e...
Пікірлер: 106
"для тех кто возбудился услышав пайтон и компилятор" .... поздравляю вы вылечены от импотенции при помощи пайтона
@user-oj2vj4rc5f
24 күн бұрын
Обожаю
Спасибо вам за видео. Мне очень нравится ваша подача материала, всегда приятно смотреть и слушать
@t0digital
25 күн бұрын
Спасибооо!
1:16 > Поэтому у тебя такой результат получается т.к. тут речь про мой коммент под прошлым видосом, то отвечу) Я отметил в комментах что эксперемент проведен не совсем ЧИСТО и что кеши МОГЛИ повлиять на результат P.S. Я не из тех адептов которые пишут что ты неправильно итерации посчитал P.S.S. Братан, хорош, давай, давай, вперёд! Контент в кайф, можно ещё? Вообще красавчик! Можно вот этого вот почаще?
Раньше об этом не задумывался, а сейчас прошёлся по операциям и понял даже как посчитать разницу в итерациях, по сути в этих примерах отличается только количество раз прохождения точки вхождения в циклы, а именно там создаётся переменная
Это как после того что в питоне нет переменных - понял) Надо смотреть, будет больно, но полезно)
Вот от while на счетчиках я вообще такой подставы не ожидал. Т.е, у for под капотом будет создаваться итератор, который на каждой итерации будет илдить новый элемент. И все это это будет завернуто в try/except, чтобы ловить StopIteration. И все вот это дело быстрее чем счетчики в while?... Я аж у себя проверил - реально быстрее. Я так понимаю, тут прикол в том, что в случае со счетчиками в while мы там руками прописываем инкременты, а вот в случае for - там это уже прописано где-то глубоко под сишным капотом. Но это все равно какая-то прям мистика. Респект за такой видос!
Спасибо за видео! Справедливости ради хочется отметить, что если в цикле производится какая-то операция, по которой определяется условие его выхода, то while все же может быть быстрее :) К примеру, если часть вычислений уровнять (по примеру с видео): from functools import partial import timeit def for_loop(count: int): counter = 0 for _ in range(count): counter += 1 def while_loop(count: int): counter = 0 while counter counter += 1 x = timeit.timeit(partial(for_loop, 1_000_000), number=100) y = timeit.timeit(partial(while_loop, 1_000_000), number=100) print(x, y, x / y) #x=2.7985 y=2.026 d=1.381 то while окажется быстрее до 40%
Если уж говорить про декартово произведение, то тут вложенный цикл и не нужен, можно обойтись одним for row, col in product(range(5), range(2)):
@t0digital
25 күн бұрын
можно
@t0digital
25 күн бұрын
Погонял тесты - работает медленнее, чем вложенные циклы с меньшим внешним циклом. Потому что будет больше проходов по данным. Сначала чтобы собрать пары, потом чтобы пройти пары и что-то с ними сделать.
лайк чисто за разбор байткода, многим будет интересно(наверное), тк, полагаю, мало народу вообще смотрит, как оно работает под капотом
Минуточку! Эдак и до спецификации не далеко.
Спасибо за пояснение к видео! А то алгопрогерский опыт не дал понять что имелось в виду под итерацией
Алексей, спасибо за детальны разбор. Такой момент, мной, замечался ранее, около года назад. Но, вы, разобрали на достаточно детальных примерах. Интересно :))) Спасибо
Долго не мог понять, почему количество итераций разное, а потом написал эти 2 цикла и сразу понял)
надо перевести столь удачный термин на английский и попробовать на SO спросить, интересно что подумают.
13:03 вообще, оч хороший пример, потому что в нем играют роль не только сами накладные расходы на циклы, но и, например, всякие кэши и конвейеризации на процессоре. Поэтому компиляторы многих языков циклы разворачивают по возможности или "подразворачивают", то есть в теле цикла пишут N раз одно и тоже, и количество итераций делят на N
Автор ответил на все возникшие у меня при просмотре каверзные вопросы. Подписался. При этом укрепился в мнении, что python - язык, что позволяет быстро писать медленно работающий код.
@t0digital
23 күн бұрын
Python на данный момент самый медленный из большой скриптовой тройки Node, PHP, Python. Но работа над его ускорением идет. Обещается пятикратный рост скорости. JIT вот завозят в 3.13
Картинка офигенная)
@t0digital
23 күн бұрын
Спасибо!
Крутой материал! Это же все началось с «чистого кода», если не ошибаюсь? И вона куда занесло ) Я периодически начинаю переходить на плюсы или ржавчину, когда думаю о скоростях исполнения, но когда вспоминаю, сколько там всего надо написать, чтобы решить простую задачку, все проходит )
То что цикл несет дополнительные издержки, ясно уже интуитивно просто потому, что цп приходится исполнять инструкции оператора for
У меня есть подозрение, что самая тяжёлая операция в байткоде в начале - это GET_ITER, или что-то типа того. И получается, что итератор (а не итерация) создаётся либо 6 раз, либо 101 раз. Классическая тема, по-моему, просто питон это скрывает от пользователя. Скорее всего, если повторить этот тест с вложенными while со счётчиками, разницы не будет. Проверять я это, конечно, не буду, потому что мне лень :D
Так эти накладные расходы никак не скалируются, т.е. если мы добавим в цикл какое-то полезное действие(sleep(0.1)), то эти десятитысячные секунды так и останутся десятитысячными, а время выполнения будет примерно одинаковым. Правильно же?
@t0digital
25 күн бұрын
Да, об этом есть в выводах. Но 0.1с это ооочень до фига даже для медленного питона
13:02 ну я так понимаю это из-за использования итераторов в for циклах в питоне на каком си c циклом for (int i = 0; i короч, любое телодвижение - это процессорное время любое телодвижение x10^(10) - это фатальное процессорное время
@xeqxeq4287
25 күн бұрын
Вот с Си не так всё очевидно. Если внутренний цикл поместится в кеш... Короче, надо конкретный случай смотреть и как оптимизирует компилятор(может вообще цикл развернуть)
@mikeofs1304
25 күн бұрын
В данном случае что там что там будет разнится. потому что циклы это джампы(гоуту кому как нравится), а это лишняя операция не самая легкая
@harry-smith404
25 күн бұрын
@@xeqxeq4287 >Короче, надо конкретный случай смотреть это в любом языке, если ковыряться в этом, то надо разбираться, во что в итоге превращается исходный код
Если вы побудите в комьюнити других языков, то там не редко говорят, что в питоне очень хреново работает for и while. На том же C C++ в разы лучше, ну ладно это такое себе сравнение. В целом лучше избегать циклы в питоне. : )
@nakamasama
25 күн бұрын
А потом и сам Питон, а после и вообще всё программирование избегайте... Чем Вы предлагаете заменить циклы? Goto и спагетти-кодом?
@dmitry-lz1ny
25 күн бұрын
@@nakamasama Так я не писал, что отказываться надо. Если их можно избежать, то лучше делать именно так. В любом случае те кто часто работает с математикой, те используют нумпай и аналоги (ну дата сатанисты) А отказаться от питона это будущее проекта, который превращается в хайлоад : )
@nakamasama
25 күн бұрын
@@dmitry-lz1ny , крутой ответ. Согласен, переход на другой язык качественно улучшает проект👍. И про нампай не поспоришь... Он ведь на С++ написан, а в Питоне просто оболочку имеет. Простите, но лично для меня избегать и отказаться в общем понимании значат одно и то же.
Я нубас, но коммент и лайк для продвижения. Ничего не пойму, но посмотрю
добвлю так чисто хрень, во время прыжка по инструкциям часто происходит промах в кэш и тд, но питон очень дубовый в этом плане, если эту задачу на си написать, то будет работать ровно наоборот. контекст внутреннего цикла будет храниться внутри кэша. чем больше прыжков по инструкциям сделать, тем больше вероятность, что другой код (браузер какой-нибудь) вытеснит контекст задачи и придется снова лезть в оператику.
Что что в названии написано? 😂
@t0digital
25 күн бұрын
Вся суть! База!
модель клавиатуры подскажи плс.
@t0digital
25 күн бұрын
keychron k3
Хорошо жить одному в просторном доме и заниматься программированием
@t0digital
25 күн бұрын
мне тоже нравится
@user-zl5sp9yh1n
25 күн бұрын
@@t0digital А нам нравится добротный контент который получается в такой хорошей обстановке.
@PlayGameToday
25 күн бұрын
@@t0digital Да, особенно хорошо, когда живешь один, нет жены, которая пилит. Молодец, я такой же.
Не подскажете, что за тема у вас в vim? На гитхабе лежит ваш старенький vim.rc с Oceanic-Next, а тут явно что-то покрасивше)
@t0digital
23 күн бұрын
gruvbox - хороша:)
если нужно использовать циклы в питоне - нужен не питон (c)
В том, что цикл несет накладные расходы, никто не спорит. В чем основной посыл - вложенный цикл с большим плечом ВНТУРИ медленнее вложенного с большим плечом снаружи. Верно? (Ну или наоборот - неважно.) Тогда предлагаю вам сделать такой эксперимент: меньшее плечо - 100, большее 1_000 (1 тысяча). Внутри самого внутреннего цикла какая-то емкая задача, скажем на 1 милисекунду - вызов CPU-bound функции, с подобранным временем примерно в 1мс, но всегда с одинаковыми данными, чтобы время работы не менялось. Теоретически прогон должен занять 100 * 1000 = 10_000 мс, то есть 10 секунд на саму работу ПЛЮС накладные расходы на циклы. Вот и сделать прогон для внешнего цикла с большим плечом, и с малым, и посмотреть, насколько разница в организации циклов будет влиять на результат, и будет ли она заметна вообще. А то еще окажется, что разница при изменении порядка циклов окажется 0,001% от всего времени работы программы, что делает усилия по такой оптимизации бессмысленными.
@t0digital
25 күн бұрын
Я говорю в тч об этом в видео.
@AlexanderBorshak
25 күн бұрын
И если разница при таком тесте будет стремиться к нулю, то это - полагаю - и есть причина, по которой итерацией считают именно ЕДИНИЦУ РАБОТЫ, а не инициализацию и обновление счетчиков циклов. Затраты на инициализацию _в абсолютном большинстве случаев_ мизерны, и их просто не учитыват. В тех же случаях, когда они играют роль, обычно оптимизируют программу вплоть до ручного размещения памяти в регистрах - и это явно не случай Питона. Rust, C, Zig - да. Но не Питон, здесь это явная "экономия на спичках".
@t0digital
25 күн бұрын
@@AlexanderBorshak нет, не в абсолютном большинстве. 1 миллисекунда, о которой вы говорите - это до хрена даже для пайтон
@AlexanderBorshak
25 күн бұрын
@@t0digital Скажем так: оптимизация с разворотом порядков цикла будет иметь смысл только в том случае, если время, затрачиваемое на инициализацию цикла сравнимо по времени с _единицей работы_ внутри этого цикла. Если разница будет уже на порядок (на единицу работы 10 нано-секунд, на инициализацию 1 нано-секунда, время взято с потолка), а тем более на порядки (100 к 1), то такая оптимизация уже теряет смысл. Намного лучше писать с учетом сложности алгоритмов - например, возможно, где-то использовав хеш-таблицу вместо массива (словарь вместо списка в терминах питона) мы сможем уменшить сложность алгоритма от квадратичной до линейной. Такая оптимизация намного важнее, чем попытка сэкономить 10 лишних наносекунд.
@mikeofs1304
25 күн бұрын
@@AlexanderBorshak Раскажи как ты собрался, например в расте, размещать "память в регистрах".
Вот это разбор - просто до атомного уровня!
31:09 я один заметил, что второй раз функция выдала -44% это как? Отрицательные значения мы понимаем, но каким образом при равном подходе (когда малый всегда значительно меньше большого) получилось отрицательное значение? (неужели где-то в кэше/еще чем-то происходит магия?) Вопрос без попытки в чем-то обвинить, просто рил не понял как такое произошло.
@t0digital
25 күн бұрын
Такие измерения всегда надо проводить на множестве запусков, чтобы как раз исключить такие флуктуации. Почему оно возникло - какие-то действия в системе возможно наложились. Работает много процессов, им ОС выделяет время, может что-то в этот момент более приоритетное запускалось в этот момент по мнению ОС и это привело к такому результату
Тоже несколько лет назад спорил. Мне доказывали, что копирование файла с индикатором прогресса выполняется быстрей, чем без него 😂😂😂😂
@t0digital
24 күн бұрын
А если отображение прогресса сделать в формате красивого на лету генерируемого 8к-видео с водопадом, птичками, листочками и мошками, чтоб комп просто визжал как сволочь - тогда файлы вообще копироваться мгновенно станут!
С итерациями все правильно, у каждого цикла свои итерации, почему они дожны объединяться? А если у тебя в одном цикле два вложенных разной длинны? Это сколько тогда у них в сумме итераций? :D
Твоя ошибка действительно в использовании слова итерации. Но в целом та ты прав. Если посмотреть на байт код, то циклы не бесплатные. 9-я и 10-я строки стоят сразу нескольких операций. Тело двух циклов выполняется 500 раз, но для того, чтобы выполнить 500 раз тело двух циклов в первом случае запуск двух циклов занял 600 раз - 10-я строка выполнялась 100 раз и 11-я выполнялась 500. Во втором случае запуск внешнего цикла происходил 5 раз, а внутреннего 500 раз. А глядя, во что превращается каждая из строк for, должно быть видно, на сколько получилась разница. Меньше запусков for приводит к экономии.
@Vorono4ka
25 күн бұрын
Почему слово итерация не подходит? Как минимум каждый из циклов итерируется по range'у, это уже можно посчитать как итерации (как автор видео и сделал с помощью my_range), или в названии операции в байт-коде питона тоже ошибка?😂
ну ассемблер это тоже не то что исполняется процессором. и никаких циклов вообще не существует, у проца все эти циклы это всегда какое то сравнение (CMP, TEST) и условный переход (JE JZ JNE JNZ...) байты этих команд можно поискать в IA32 Arch от intel, это и будут те самые байты которые проц выполняет. Ежу понятно что чем больше команд надо выполнить тем дольше работает программа, если внешний цикл меньше то как минимум команд сравнения будет выполнено меньше, а значит и работать будет быстрее, но процы умеет в предсказание переходов (привет SPECTRE и MELTWOWN и уже с нмни) и научились заренне исполнять код, так что это даже микро-оптимизацией наверное не стоит считать, скорее всего это вообще не особо актуально.
@t0digital
25 күн бұрын
Да, и об этом тоже есть в этом видео:)
Вот его торкнуло 🤣🤣🤣 Мужик выдыхай!!! 😁😁😁
@AlexanderBorshak
25 күн бұрын
)))
Фрикциями?
@t0digital
25 күн бұрын
Да!
@baloobear70
25 күн бұрын
@@t0digital это не может не радовать, дорогие друзья 😃
Не хочу ставить лайк. Всего лайков 128. Что-то это да значит) Пусть значит. P.S.: Обновил страницу, чтобы всё таки поставить лайк. На этот раз лайков было уже 193)
имхо выгода от такой оптимизации будет стремиться к нулю, если внутри цикла будет стоять что-нить cpu-bound
@t0digital
24 күн бұрын
Об этом есть в видео
А всё почему, а всё потому, что пайтон не очень оптимизирует, я сейчас вбил аналогичный код в C++, так вот он мне внутренние циклы просто свернул до умножения простого P.S. Писать то я всё равно продолжу на любимом питончике, но, конечно, его оптимизирующему компилятору до компилятора а-ля clang или gcc ой, как далеко
@t0digital
25 күн бұрын
В 3.13 вроде jit завезут:)
@Verdgil
25 күн бұрын
@@t0digitalне ну jit это лишь первый шаг, сишные компилятор просто свернул код до умножения 5*100 в одном случае и 100*5 во втором P.S. clang 15, apple version, O3
@t0digital
25 күн бұрын
Да, Сишка есть Сишка
@Verdgil
25 күн бұрын
@@t0digitalи не поспоришь
@Verdgil
25 күн бұрын
@@t0digitalи не поспоришь... верим в развитие нашего любимого языка
Жду новых комментариев о том, что опять кто-то не прав и вообще всё не так и сам Гвидо говорил, что... 😂
@t0digital
25 күн бұрын
Гвидо, молчи, Гвидо, не надо😂
@user-zl5sp9yh1n
25 күн бұрын
Тут на ютубе есть парень который принимает участие в разработке питона, может у него спросите?😂
Все правильно раскидал, и пример нормальный. Дурачки предлагающие размыть тяжелой операцией внутри - сводят одну задачу к совершенно другой. А итерация в твоем контексте - это один джамп в машинном коде, все отсальное это уже из совершенно других контекстов. И унижение этих "философов от cs'" при помощи байткода считаю абсолютно правильным. ЛайГ. И кстати индусский пример таки - пример трейдофа скорости на памятью И то же дичайше поддерживаю его добавление в методических целях
@andreymironov8169
25 күн бұрын
То есть питоновский FOR_ITER сводится в итоге к jump? Не увидел такого в видео. А тяжелая операция показывает, что оптимизация с малым и большим циклом бессмысленна, тем более в питоне, который вообще не про скорость 😊
@mikeofs1304
25 күн бұрын
@@andreymironov8169 раскажи мне чем на уровне машинного кода можно реализовать цикл кроме джампа? Спойлер loop это тоже джамп. Тяжелая операция это вырожденный случай общего. Или ьы не понимаешь таких слов?
@andreymironov8169
25 күн бұрын
@@mikeofs1304 где питон, а где машинный код?
@mikeofs1304
25 күн бұрын
@@andreymironov8169 в компьютере оба
@andreymironov8169
25 күн бұрын
@@mikeofs1304уровень умника понятен... иди котиков посмотри, они тоже в компьютере
34 минуты унижения адептов аптимызации в сфере веб технологий!
Ахахахахаха
все функции с операциями сложения больших чисел лучше компилировать на Cython например для цикла инкрементирующего count 222222222 раза результаты: Cython: 5.83939254283905e-07 Python: 7.961033541010693 Cython выполнился быстрее на 1_363_332_384.09%