Дизассемблируем Python-циклы. Ассимиляция Гиперпространственного Фокала Социалистических Измерений!

preview_player
Показать описание

Препарируем 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 года и на этом канале делюсь своим опытом. Я основатель и руководитель компаний:

Комментарии
Автор

"для тех кто возбудился услышав пайтон и компилятор" .... поздравляю вы вылечены от импотенции при помощи пайтона

КоньЛюдоед-фф
Автор

1:16
> Поэтому у тебя такой результат получается
т.к. тут речь про мой коммент под прошлым видосом, то отвечу) Я отметил в комментах что эксперемент проведен не совсем ЧИСТО и что кеши МОГЛИ повлиять на результат

P.S. Я не из тех адептов которые пишут что ты неправильно итерации посчитал
P.S.S. Братан, хорош, давай, давай, вперёд! Контент в кайф, можно ещё? Вообще красавчик! Можно вот этого вот почаще?

losos
Автор

Спасибо вам за видео. Мне очень нравится ваша подача материала, всегда приятно смотреть и слушать

NN-jxkb
Автор

Раньше об этом не задумывался, а сейчас прошёлся по операциям и понял даже как посчитать разницу в итерациях, по сути в этих примерах отличается только количество раз прохождения точки вхождения в циклы, а именно там создаётся переменная

roldexX
Автор

Вот от while на счетчиках я вообще такой подставы не ожидал.
Т.е, у for под капотом будет создаваться итератор, который на каждой итерации будет илдить новый элемент. И все это это будет завернуто в try/except, чтобы ловить StopIteration.
И все вот это дело быстрее чем счетчики в while?...
Я аж у себя проверил - реально быстрее.
Я так понимаю, тут прикол в том, что в случае со счетчиками в while мы там руками прописываем инкременты, а вот в случае for - там это уже прописано где-то глубоко под сишным капотом.
Но это все равно какая-то прям мистика.
Респект за такой видос!

IdE
Автор

Спасибо за видео!
Справедливости ради хочется отметить, что если в цикле производится какая-то операция, по которой определяется условие его выхода, то 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 < count:
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%

Роман-чл
Автор

Это как после того что в питоне нет переменных - понял) Надо смотреть, будет больно, но полезно)

Vjidowkdkcpapqkfjfw
Автор

Если уж говорить про декартово произведение, то тут вложенный цикл и не нужен, можно обойтись одним
for row, col in product(range(5), range(2)):

merzbow
Автор

Автор ответил на все возникшие у меня при просмотре каверзные вопросы. Подписался.
При этом укрепился в мнении, что python - язык, что позволяет быстро писать медленно работающий код.

shaltaybltay
Автор

13:03 вообще, оч хороший пример, потому что в нем играют роль не только сами накладные расходы на циклы, но и, например, всякие кэши и конвейеризации на процессоре. Поэтому компиляторы многих языков циклы разворачивают по возможности или "подразворачивают", то есть в теле цикла пишут N раз одно и тоже, и количество итераций делят на N

ВиталийЕмельянов-жг
Автор

Долго не мог понять, почему количество итераций разное, а потом написал эти 2 цикла и сразу понял)

АлексейПаршин-че
Автор

В том, что цикл несет накладные расходы, никто не спорит. В чем основной посыл - вложенный цикл с большим плечом ВНТУРИ медленнее вложенного с большим плечом снаружи. Верно? (Ну или наоборот - неважно.) Тогда предлагаю вам сделать такой эксперимент: меньшее плечо - 100, большее 1_000 (1 тысяча). Внутри самого внутреннего цикла какая-то емкая задача, скажем на 1 милисекунду - вызов CPU-bound функции, с подобранным временем примерно в 1мс, но всегда с одинаковыми данными, чтобы время работы не менялось. Теоретически прогон должен занять 100 * 1000 = 10_000 мс, то есть 10 секунд на саму работу ПЛЮС накладные расходы на циклы. Вот и сделать прогон для внешнего цикла с большим плечом, и с малым, и посмотреть, насколько разница в организации циклов будет влиять на результат, и будет ли она заметна вообще. А то еще окажется, что разница при изменении порядка циклов окажется 0, 001% от всего времени работы программы, что делает усилия по такой оптимизации бессмысленными.

AlexanderBorshak
Автор

То что цикл несет дополнительные издержки, ясно уже интуитивно просто потому, что цп приходится исполнять инструкции оператора for

km-academy_ru
Автор

У меня есть подозрение, что самая тяжёлая операция в байткоде в начале - это GET_ITER, или что-то типа того. И получается, что итератор (а не итерация) создаётся либо 6 раз, либо 101 раз. Классическая тема, по-моему, просто питон это скрывает от пользователя. Скорее всего, если повторить этот тест с вложенными while со счётчиками, разницы не будет. Проверять я это, конечно, не буду, потому что мне лень :D

john.darksoul
Автор

надо перевести столь удачный термин на английский и попробовать на SO спросить, интересно что подумают.

AureliusLuminis
Автор

Спасибо за пояснение к видео!
А то алгопрогерский опыт не дал понять что имелось в виду под итерацией

notacatbeaver
Автор

Алексей, спасибо за детальны разбор. Такой момент, мной, замечался ранее, около года назад. Но, вы, разобрали на достаточно детальных примерах. Интересно :))) Спасибо

gnompirogov
Автор

лайк чисто за разбор байткода, многим будет интересно(наверное), тк, полагаю, мало народу вообще смотрит, как оно работает под капотом

harry-smith
Автор

Хорошо жить одному в просторном доме и заниматься программированием

PlayGameToday
Автор

Твоя ошибка действительно в использовании слова итерации. Но в целом та ты прав. Если посмотреть на байт код, то циклы не бесплатные. 9-я и 10-я строки стоят сразу нескольких операций. Тело двух циклов выполняется 500 раз, но для того, чтобы выполнить 500 раз тело двух циклов в первом случае запуск двух циклов занял 600 раз - 10-я строка выполнялась 100 раз и 11-я выполнялась 500. Во втором случае запуск внешнего цикла происходил 5 раз, а внутреннего 500 раз. А глядя, во что превращается каждая из строк for, должно быть видно, на сколько получилась разница. Меньше запусков for приводит к экономии.

programisli