@@George-el9mn при чем если дать компилятору gcc ключ оптимизации, то тут уже тебе нужно иметь хорошие такие познания, чтобы оптимизировать код лучше компилятора. Бывали случаи, когда даже дяди их intel которые работали над оптимизациями писали на ассемблере код, который был медленнее скомпилированного
Разница все же небольшая есть в начальных циклах while и for: - в цикле while выполняется увеличение значения переменной "result" на 1 - в цикле for выполняется увеличение значения переменной "result" на значение переменной "num" Другими словами, функции loop1() и loop2() вернут разные значения.
5:50 если совсем коротко, в Си for появился просто для упрощения работы программистов (синтаксический сахар), на деле же грубо говоря это просто обёртка цикла while. Ну, не с помощью LOOP циклы реализовываем, и на том спасибо)
Забавное видео, я не знаком с питоном совсем, чего не скажу про си. Так вот разница в них в том, что питон это интерпретируемый язык, а си - компилируемый. И скорость выполнения циклов такая потому что в си есть компилятор и такие циклы он оптимизирует на раз, даже без флага -o3 (максимальная оптимизация), т.е. весь цикл фактически уменьшился до строчки result = value. поэтому время выполнения и занимало 0-1с, никакой магии тут нет. чтобы программа честно пробегала миллион итераций можно добавить спецификатор volatile для переменной result или i, это укажет компилятору, что эта переменная может измениться вне программы и оптимизировать ее нельзя, т.е. программа каждый раз будет вызывать эту переменную из ПАМЯТИ при каждом обращении, что усложняет задачу и сравнивать такой цикл с питоновским неверно, т.к. итераторы скорее всего программа будет держать в регистре, а не в памяти, что так же увеличивает скорость работы программы! И да нет разницы между циклами for и while в целом (на Асемблере) и в частности в си, в самом начале языка Си цикла for не было вообще и не зря надо написать for(;;) чтобы запустить бесконечный цикл, я думаю это типо глобального макроса, который раскрывается в while по итогу, и при переводе на ассемблер команды не меняются ни при for ни при while, меняется лишь "интерфейс" для программиста. Дальше идут мои предположения, не претендующие на истину в 10 инстанции! В питоне разница же происходит, как ты правильно сказал за счет того, что range написан на Си, т.е. на строго типизированном языке, поэтому команды num += 1 на Си и Питоне будут выполняться за разное время, т.к. на Си это всегда переменная определенного типа, а в питоне нет, поэтому под капотом команды num +=1 интерпретатор сначала смотрит что за тип был, какой тип мы плюсуем и выполняет команду и в цикле while он делает это каждый раз для двух переменных num и result, в цикле for он это делает 1 раз для переменной num, и каждый раз для переменной result. Хотя могу ошибаться тут, т.к. питон не изучал...
Quick review: самый быстрый способ итерировать в пайтон - не итерировать в пайтон. Ты использовал разные функции: где-то ты считал сумму чисел до ста миллионов, а где-то количество чисел до ста миллионов. Вещи разные, и потому как интерпритатор пайтон, так и компилятор gcc будут по разному оптимизировать код. С большой долей вероятности, gcc съел весь твой цикл, так как увидел константные литералы и цикл по ним, и выдал предкомпилированный ответ.
Попробуйте return (i * (i - 1)) // 2 Numpy - очень крутая библиотека. Лежит в основе кучи фреймворков для дата-аналитики и ML. Вот только arange(100000000) аллоцирует соответственно бооооольшой блок в памяти. А для быстрых вычислений, кстати, можно ещё Numba или что-то другое с JIT-компиляцией использовать. А если ещё и с распределенными вычислентями на видеокарте... Мммм пушка-гонка))
В цикле while Вы не вычисляете sum(range(value), Вы только увеличиваете rezult+=1, а в цикле for Вы действительно вычисляете sum(range(value). Сравнение не совсем корректно.
Если расскажешь о методах ускорения питона с помощью си это будет огонь! Не знал, что можно писать свои модули на си, теперь появилось много разных мыслей/идей Спасибо!)
Вывод времени ровно 0 секунд или ровно 1 секунду или ровно 2 секунды намекает, что код обрабатывает разницу между промежутками времени как int, поэтому точность измерения до 0,9(9) секунд.
На С неважно for или while, все примерно одинаково работают по скорости выполнения. Но чтобы на С корректно замерить время выполнения цикла, надо итоговую сумму в конце вывести на экран, тогда компилятор сделает честный код. При этом важно поставить опцию компиляции -O2 (можно и -O3). Однако, если у Вас простейший инкремент, то умный компилятор может свернуть его самостоятельно вне зависимости от вывода результата на печать :) Возможно, лучше тестировать сумму арифметической прогрессии, чтобы компилятор не смог распознать зависимость. На С/С++ легко наступить на грабли и требуется некоторое время на составление корректного теста.
Только благодаря усилиям компиляторе🥴 так то прыгающие ифовые инструкции в форе дадут неплохую просадку. И вроде как вайл здесь спасение. Но вот в дело вступит оптимизатор компайла и вся оптимизация в выборе цикла ни о чем🥴
да кстати у питона есть API для С поэтому можно что-то написать на С а потом использовать в питоне благодаря чему некоторые библиотеки фреймворки написаны на C, C++ а какая у тебя тема в VS code? понравилась довольно приятная
есть некоторые подозрения, вот прям как-то на панель задач в видео смотрю, и вот прям шестое чувство подсказывает что это PyCharm. Не знаю почему у меня такое ощущение...
Цикл фор в си сделай по другому: for(int i=big_num; i; --i){ /*do your stuff*/ } так будет на 10-20% быстрее Все дело в том что на ассемблере у тебя генерится сравнение в виде операции cmp которая автоматически генерит флаг состояния по все видам сравнения (> < и ТД и та) и потом генерится код с необходимым джампом. Если по моему то просто проверка на (не ноль) при флаге -О3. Есть ещё более извратный вариант: for(int i=big_num/4; i; --i){ /*do your stuff 1*/ /*do your stuff 2*/ /*do your stuff 3*/ /*do your stuff 4*/ } for(int i=big_num%4; i; --i){ /*do your stuff*/ } итоговая производительность должна возрасти в 2раза минимум Насчёт ситайпс - сделай. Развлекайся)))
"итоговая производительность должна возрасти в 2раза минимум" Итоговая производительность в первую очередь зависит от тела цикла. В большинстве реальных задач затраты на итерирование на порядки меньше затрат на выполнение тела цикла. Первый вариант, с обратным итерированием в реальности будет применим далеко не всегда. Про второй лучше вообще не упоминать.
Неоднозначное видео. Автор, естественно, не врёт, особенно если понять, что во второй функции просто небольшая опечатка. Там нужно убрать у result += num знак плюса, вы получите похожие значения. Т.е. название видео полностью оправданно, при работе с пайтоном нужно помнить, где можно сэкономить время. Но с другой стороны важно понимать принципиальную разницу двух циклов. В данной ситуации вы просто пытаетесь использовать цикл while взамен цикла for. Однако они имеют разное значение в программировании. While изначально не подразумевает работу с такими большими значениями, и он работает непосредственно с true и false, т.е. входные параметры можно сделать любые, сравнивать хоть текст, хоть логическое, хоть цифру. А цикл с параметром изначально задуман как счётчик. Можно ли заменять эти циклы друг другом? Да. Но зачем? В теории можно ходить на руках и брать ногами вилку. Но есть ли в этом смысл? Не думаю.
Это называется тайп хинты (type hint). Они нужны чтобы подсказать програмисту и среде разработки какой тип данных возвращает данная функция. Чисто для удобства существует. Записывается: def my_function() -> :
Я уже являюсь евангелистом PyPy. Хожу и всем рассказываю про него и многие не слышали про PyPy. Это версия пайтона написана на пайтоне и к нему прикручен JIT компилятор. Давайте посмотрим. У меня loop1 на обычном пайтоне выполняется за 12.566052499998477, а вот на PyPy за 0.1593255. То есть фактически мгновенно. Для тестов надо запускать файл pypy3.9.exe Я на PyPy как раз делаю такие вещи, где надо чего-то подсчитать, он справляется очень и очень шустро.
если в первом случае в def loop1() убрать (не нужный) result+=num то скорость сравняется со вторым вариантом, где используется range более того, если в def loop2() нагрузить первым бессмысленным действием (result+=num) то и скорость выполениея с range будет примерно такой же как и без range вывод : первые два примера имеют разницу в быстродействии аж 13% ( что имхо малосущественно ), остальные ещё не смотрел, но по общему мнению(что тоже требует проверки) numpy быстрее обычного питона на порядок, что уже существенно
7:16 - а если компилировать с флагом оптимизации `-O3` весь цикл сворачивается в формулу. Цикл на 1е15 итераций отрабатывает за 3мс (время запуска, выполнения и завершения программы с выводом результата в консоль). Куда интересней просуммировать последовательность, как в python `sum(range(10**8))`, т.е. `sum += num` а не `sum++`.
Такое `-O3` тоже свернет в num * (value * (value + 1)) >> 1. Автор, изучи, пожалуйста, опции оптимизации gcc прежде чем бенчмаркать! Используемый метод time имеет точность в секунду, вы не могли не заметить этого, так что замеры там практически бесполезны, можно использовать clock(), в C++ есть вариант получше -- std::chrono
Настоящая интеграция с C - это Pytex/Cython. Питоновский код транслируется в C и компилируется gcc, а синтаксическое надмножество Pyrex имеет плюшки вроде статической типизации, структур и указателей.
Привет, спасибо за видео. Используешь ли пайчарм? Стоит ли к нему привыкать заранее даже тогда, когда весь его функционал не нужен, или считаешь лучше на него пересаживаться только по надобности?
Все очень интересно, но ничего не ясно. Так по видео же сравнивается арифмитические операции суммировани, а не сами циклы как таковые. Допустим какая польза мне от sum() если мне нужно перебрать словарь? А вот видос как прикрутить C к python для ускорения будет полезно
Программы не совсем идентичные. В программе на С оптимизатор видит что result никак не используется никуда не передаётся и просто не запускает цикл, вообще все что связано с result в скомпилированный код не попадает, поэтому программа выполняется действительно мгновенно.
Если посмотреть итоговое значение, которое выдаётся при использовании цикла с модулем Numpy, то оно отличается от того, что выдают первоначальные (имеются ввиду циклы в которых рассматривается именно арифметическая прогрессия), если понизить число итераций со 100000000 до 10000, то всё будет работать так как задумывалось автором изначально. Такие вещи нужно проверять на практике, а не слепо верить, что показанный вам материал в подобных видео, будет работать как часы.
Точно, по умолчанию он считает элементы int32 поэтому у него будет происходить переполнение НО при этом никаких ошибок не выходит. Чтоб правильно считалось нужно записать так numpy.arange(100_000_000, dtype=numpy.int64) время при этом практически не меняется. Но это не единственная проблема, смотри мой самый свежий комментарий.
Почитал новости, оказывается, началась работа над ускорением CPython. Версия 3.11 будет в разных случаях от 10% до 60% быстрее, чем 3.10. Что было сделано: 1) Faster Runtime. 2) Inlined Python function calls 3) Specializing Adaptive Interpreter Собираются в течение пару лет прикрутить JIT, что уже сделано на PyPy
Измерения через функцию time() в С или C++ некорректны, т.к. точность этого метода только лишь до секунды. Нужно использовать более точные функции, а не time(). Т.к. у Вас какая-то linux образная ОС, то можно было бы использовать clock_gettime(). Ну либо стандартную clock() уже... А то, что у Вас получалось либо 0 либо 1 секунда, то это лишь потому, что момент запуска совпадал с переходом с предыдущей секунды на следующую...
Замеры скорости работы функции в С вызывает большие сомнения, неужели она выполняется ровно 1 сек или 2 сек с точностю до 6 знака? Там ведь что-то точно округляется, и невозможно сравнить скорость с суммой из Numpy. А в целом, я думаю было бы круто ещё сравнить с Just-In-Time compilation из библиотеки Numba.
Да, в C можно сделать так: #include #include clock_t begin, end; double elapsed; begin = clock(); /* Код, время выполнения которого мы хотим замерить */ end = clock(); elapsed = (double)(end - begin) / CLOCKS_PER_SEC; printf("Elapsed %lf sec ", elapsed);
он ничего не округлял, time_t имеет точность в секунду, поэтому никаких мс быть не может. Обычно для замеров времени выполнения программы в Linux ее запускают с помощью утилиты time, тогда точность совсем другая. Но тут проблема совсем не в точности замера, а в непонимании работы компилятора. Подробнее я написал тут в коментарии
А теперь давайте представим, что есть такая библиотека, как numba и попробуем воспользоваться её функцией njit, в роли декоратора. import timeit import numpy as np from numba import njit value = 100_000_000 def loop1(num=0, result=0) -> int: while num < value: result += num num += 1 return result def loop2(result=0) -> int: for num in range(value): result += num return result def loop3() -> int: return sum((num for num in range(value))) def loop4() -> int: return sum([num for num in range(value)]) def loop5() -> int: return sum(range(value)) def loop6() -> int: return np.sum(np.arange(value)) @njit def loop7(num=0, result=0) -> int: while num < value: result += num num += 1 return result @njit def loop8(result=0) -> int: for num in range(value): result += num return result print(f"loop1: {timeit.timeit(loop1, number=1)}") print(f"loop2: {timeit.timeit(loop2, number=1)}") print(f"loop3: {timeit.timeit(loop3, number=1)}") print(f"loop4: {timeit.timeit(loop4, number=1)}") print(f"loop5: {timeit.timeit(loop5, number=1)}") print(f"loop6: {timeit.timeit(loop6, number=1)}") print(f"loop7: {timeit.timeit(loop7, number=1)}") print(f"loop8: {timeit.timeit(loop8, number=1)}")
Аннотация, указывающая на тип данных который должна возвращать функция. Нужна в основном просто для того, чтобы лучше ориентироваться в коде. Работа функции от этого не меняется
Это аннотация типа для линтеров. Она никак не влияет на компилятор, он просто проигнорирует эту часть строки. Зато, если подключены линтеры, они смогу выдать предупреждение о несоответствии типа возвращаемого значения с ожидаемым.
Маленькое дополнение по NumPy после моих экспериментов с ним: 1. У автора вычисляется НЕ верная сумма, т.к. он использовал arange по умолчанию, а по умолчанию он считает элементы int32 поэтому у него будет происходить переполнение НО при этом никаких ошибок не выходит. Чтоб правильно считалось нужно записать так numpy.arange(100_000_000, dtype=numpy.int64) время при этом практически не меняется. 2. У всего есть цена и у arange она тоже есть, и это то что в отличии от range он СОЗДАЕТ ВЕСЬ массив в памяти, т.е. все эти 100 миллионов элементов! Когда я попытался создать больший массив на 10 миллиардов он вывел ошибку что памяти не хватает и нужно 74Гб оперативки!!! Кстати 70% времени работы уходит на создание этого массива, само суммирование за остальные 30%. Т.е если бы не создавался массив, можно было бы в 2 раза еще ускорить, но я не знаю возможно ли это в NumPy,. Это точно не numpy.sum т.к. он ждет на вход только массив.
NumPy в данном случае не подходит, а вот использование numba даёт просто колоссальное ускорение. Тот же цикл ускорился так что не фиксируется таймером, пришлось поставить 4_000_000_000 и summa считать во float, только тогда время выполнения стало 0.3 секунды. Память вообще не расходуется.
Поигрался с циклами, действительно Пи очень медленный, и на нем еще написано большинство современного софта ( ( .. Придется С вспоминать) Спасибо за gcc
Нет ничего плохого использовать сразу 2 этих языка, я активно использую плюсы в тандеме с пайтоном. На пайтоне пишу простую логику где не нужны какие-то сложные вычисления, а на плюсах наоборот пишу всю сложную логику где важна скорость вычисления
Интересно Вообще все делаю с лист компрехеншен, генератор, не залупит ничего в память. Поиск по файлу займёт время, но не так долго) Зависит от количества данных. Лист комп конечно не прямо миг, но я и не МЛ специалист тоже)
Си конечно мощно. Но проще всё же на Rust ускорить пайтон. За два года коммерческой разработки бекенда, ни разу не потребовались мощные вычисления в коде. Всё до боли банально - достать из БД или положить/обновить в БД запись. Хотя. Если в главном сервисе делать агрегацию данных, т.е. полученные по шине данные из разных сервисов сложить в один json, то наверное можно и написать на Си/Раст. П.С. Была задача по расчету количества потомков у животного до 4 колена. Но это нереально делать в коде, т.к. 5млн записей в БД занимает много места. Получить запросом и обработать не выйдет. Тут опять же нужно на SQL делать. В общем, непонятно где может пригодиться.
Тыщу лайков, хмм... ну лан лайкану, мне понравился хинт что в питоше можно int записать как 100_000_000.. Я не очень знаю питон, но отлично знаю Си. По поводу numpi скажу, что стоило-бы проверить ещё ударную по потокам процессора... Сисадмины очень часто жалуются на эту либу что она глушит процак. Итерация с сложением в Си скорее не милисикунды, скорее всего это наносекунды. В конечном итоге это скорее всего 8 команд асемблера, что для процессора вообще раз плюнуть.
Дядь сделай пример со строками, например когда ты их парсишь и формируешь обьект, а не вот эта математика, которую питонисты увидят раз в жизни (в универе) а не на работе
1. Аннотация, указывающая на тип данных который должна возвращать функция. Нужна в основном просто для того, чтобы лучше ориентироваться в коде. Работа функции от этого не меняется 2. Чтобы сразу было видно, что это сто миллионов, без необходимости лишний раз считать нули. Но записывать можно как угодно
@@zproger понятное дело, но тут было сравнение скорости компилятора и интерпретатора что в корне неверно. Что касаемо внедрения кода, еще в Турбо Паскале можно было внедрять ассемблерные блоки...
И всё же, говорить что Python медленнее C - не верно, язык не может быть быстрым или медленным. Другой вопрос - имплементация решений. В решении на С не делается дополнительных проверок, копирований и т.п.
Не совсем корректно говорить что Си работает быстрей чем питон в данном контексте. Быстрей здесь работает то, что меньше затрачивает обращений к аллокатору. Тот же си если будет постоянно малочить, будет медленней чем си работающий со стеком. И питон, который дофига создаёт объектов для gc, работает медленней, чем питон, который задействует меньше объектов (скажем встроенные функции) и не создаёт лишние обращения к памяти и трековые ссылки… и скорее речь здесь стоит вести об оптимизации работы с памятью, а не сравнивать два языка имхо…
Если серьёзно, то видео - просто ерунда какая-то. Циклы предназначены, чтобы многократно выполнять наборы инструкций а не для подсчёта суммы. Для подсчёта суммы лучше и быстрее будет воспользоваться функцией sum, как раз таки. Python действительно медленный, особенно если не знаешь, что делаешь, для чего и каким инструментом для этого следует пользоваться.
Python не написан на Си, не вводите людей в заблуждение. Это язык, и у него есть имплементации, большинство на Си, но есть и другие. Jython и IronPython, например.
да ужж сравнили 2ное вычисление с одинарным), и подход нелогичный немног, тогда уж не си , а голанд дучше бы импортировали, теперь уже даж для игрушек берут многопроцессорные сокеты и процы по 20 ядер с куевым количеством потоков и язык си уже стал подтупливать, а питон в принцыпе не для быстрых задач, а для всех кроме десктопа почти)
Мало того что сами функции не идентичны, в таймите прогнал 1 раз, да ещё и sum(range(n)) называет циклом, хотя это, мягко говоря, строка кода под одну единственную задачу. Подача материала "со знанием дела"... Тот же mCoding яснее и правильнее все объяснил, пускай и без загрузки С кода.
Прикол в том, что на практике это малок кому вообще пригодится. По этому нет никакого смысла учить С, когда есть Пайтон. И да, если уж очень надо будет, то можно будет посмотреть это видео и сделать нужную функцию.
Видео по ctypes будет круто увидеть! Годный контент, давно была идея объединить с и Пайтон, но казалось сложной затеей, спасибо!
Welcome to Cython. У меня есть пара видосиков на эту тему.
Хочешь ускорить python, пиши на С, как то так я понял это видео)
=)
Или на ассемблер
@@Grey_Slime он не сильно быстрее СИ
@@George-el9mn при чем если дать компилятору gcc ключ оптимизации, то тут уже тебе нужно иметь хорошие такие познания, чтобы оптимизировать код лучше компилятора. Бывали случаи, когда даже дяди их intel которые работали над оптимизациями писали на ассемблере код, который был медленнее скомпилированного
Разница все же небольшая есть в начальных циклах while и for:
- в цикле while выполняется увеличение значения переменной "result" на 1
- в цикле for выполняется увеличение значения переменной "result" на значение переменной "num"
Другими словами, функции loop1() и loop2() вернут разные значения.
И в первой функции 2 сложения, когда во второй одно
Тоже не понял, в чем суть. Пару раз пересмотрел, пытаясь понять, что я не так понял. Оказывается, все я правильно понял.
@@chinchita5532 + там еще на каждой итерации - операция проверки условия num < value
Для получения одинакового значения нужно изменить:
num += 1
result += num
Да есть такое, только сейчас заметил что случайно эту переменную подставил 😐
5:50 если совсем коротко, в Си for появился просто для упрощения работы программистов (синтаксический сахар), на деле же грубо говоря это просто обёртка цикла while.
Ну, не с помощью LOOP циклы реализовываем, и на том спасибо)
Забавное видео, я не знаком с питоном совсем, чего не скажу про си. Так вот разница в них в том, что питон это интерпретируемый язык, а си - компилируемый. И скорость выполнения циклов такая потому что в си есть компилятор и такие циклы он оптимизирует на раз, даже без флага -o3 (максимальная оптимизация), т.е. весь цикл фактически уменьшился до строчки result = value. поэтому время выполнения и занимало 0-1с, никакой магии тут нет. чтобы программа честно пробегала миллион итераций можно добавить спецификатор volatile для переменной result или i, это укажет компилятору, что эта переменная может измениться вне программы и оптимизировать ее нельзя, т.е. программа каждый раз будет вызывать эту переменную из ПАМЯТИ при каждом обращении, что усложняет задачу и сравнивать такой цикл с питоновским неверно, т.к. итераторы скорее всего программа будет держать в регистре, а не в памяти, что так же увеличивает скорость работы программы!
И да нет разницы между циклами for и while в целом (на Асемблере) и в частности в си, в самом начале языка Си цикла for не было вообще и не зря надо написать for(;;) чтобы запустить бесконечный цикл, я думаю это типо глобального макроса, который раскрывается в while по итогу, и при переводе на ассемблер команды не меняются ни при for ни при while, меняется лишь "интерфейс" для программиста.
Дальше идут мои предположения, не претендующие на истину в 10 инстанции!
В питоне разница же происходит, как ты правильно сказал за счет того, что range написан на Си, т.е. на строго типизированном языке, поэтому команды num += 1 на Си и Питоне будут выполняться за разное время, т.к. на Си это всегда переменная определенного типа, а в питоне нет, поэтому под капотом команды num +=1 интерпретатор сначала смотрит что за тип был, какой тип мы плюсуем и выполняет команду и в цикле while он делает это каждый раз для двух переменных num и result, в цикле for он это делает 1 раз для переменной num, и каждый раз для переменной result. Хотя могу ошибаться тут, т.к. питон не изучал...
Quick review: самый быстрый способ итерировать в пайтон - не итерировать в пайтон.
Ты использовал разные функции: где-то ты считал сумму чисел до ста миллионов, а где-то количество чисел до ста миллионов. Вещи разные, и потому как интерпритатор пайтон, так и компилятор gcc будут по разному оптимизировать код. С большой долей вероятности, gcc съел весь твой цикл, так как увидел константные литералы и цикл по ним, и выдал предкомпилированный ответ.
Слушай, реально)
Да, потому что надо сумму в конце выводить на экран, и тогда он не съест. Плюс не забыть поставить опцию компиляции -O2 :)
Щас бы в первом цыкле увеличивать на 1 а во втором на арефметичческую прогресию ... автор гений
ну и далее по тексту: зачем в первом цыкле переменная num если тебе не важна какая итерация - while result
Мне кажется в первом цикле он допустил опечатку: он имел ввиду result+=num
Попробуйте return (i * (i - 1)) // 2
Numpy - очень крутая библиотека. Лежит в основе кучи фреймворков для дата-аналитики и ML. Вот только arange(100000000) аллоцирует соответственно бооооольшой блок в памяти. А для быстрых вычислений, кстати, можно ещё Numba или что-то другое с JIT-компиляцией использовать. А если ещё и с распределенными вычислентями на видеокарте... Мммм пушка-гонка))
В цикле while Вы не вычисляете sum(range(value), Вы только увеличиваете rezult+=1, а в цикле for Вы
действительно вычисляете sum(range(value). Сравнение не совсем корректно.
Не хватает действительно самого быстрого варианта - суммы арифметической прогрессии )
Легко делается
Ещё можно сделать низкоуровневых цикл бинарными операторами(но я в этом особо не шарю 😅)
@@blackcatdevel0per разве что заменить деление на два на >>
Если расскажешь о методах ускорения питона с помощью си это будет огонь!
Не знал, что можно писать свои модули на си, теперь появилось много разных мыслей/идей
Спасибо!)
Гораздо проще на Pyrex/Cython. Я рассказал.
@@m0Ray79 спасибо за инфу, надо изучить)
В первом цикле опечатка (10 строка), вы наверное имели ввиду result+=num чтобы там была прогрессия
Вывод времени ровно 0 секунд или ровно 1 секунду или ровно 2 секунды намекает, что код обрабатывает разницу между промежутками времени как int, поэтому точность измерения до 0,9(9) секунд.
На С неважно for или while, все примерно одинаково работают по скорости выполнения. Но чтобы на С корректно замерить время выполнения цикла, надо итоговую сумму в конце вывести на экран, тогда компилятор сделает честный код. При этом важно поставить опцию компиляции -O2 (можно и -O3). Однако, если у Вас простейший инкремент, то умный компилятор может свернуть его самостоятельно вне зависимости от вывода результата на печать :) Возможно, лучше тестировать сумму арифметической прогрессии, чтобы компилятор не смог распознать зависимость. На С/С++ легко наступить на грабли и требуется некоторое время на составление корректного теста.
Только благодаря усилиям компиляторе🥴 так то прыгающие ифовые инструкции в форе дадут неплохую просадку. И вроде как вайл здесь спасение. Но вот в дело вступит оптимизатор компайла и вся оптимизация в выборе цикла ни о чем🥴
Очень нужно видео о том, как передавать/забирать из си переменные в питон
Кидай через файлы)
да кстати у питона есть API для С поэтому можно что-то написать на С а потом использовать в питоне благодаря чему некоторые библиотеки фреймворки написаны на C, C++
а какая у тебя тема в VS code? понравилась довольно приятная
Тема: Dracula.
На счет Си это да, важные компоненты можно на нем писать,
особенно тот код, который надо защитить.
@@zproger благодарю
есть некоторые подозрения, вот прям как-то на панель задач в видео смотрю, и вот прям шестое чувство подсказывает что это PyCharm. Не знаю почему у меня такое ощущение...
def loop1(num = value):
result = 0
while num:
result += num
num -=1
return result
print(t())
на 28% быстрее вашего whil'a считает
2:15 Увы это совсем не образно : (. Отличный видос и информация полезная! А что у вас за дистрибутив стоит?
Привет. Можешь рассказать зачем нужен файл __init__.py ?
Написал комент на средине, но когда досмотрел до конца это просто огненно 🔥 Продолжай 👊🤝
Спасибо 🔥
Та ты гений! Превью видео даже не преувеличено, а так как есть, лови респект
😎
А какой у вас Linux? Крутой видос
Zorin OS
@@zproger спасибо:)
Цикл фор в си сделай по другому:
for(int i=big_num; i; --i){
/*do your stuff*/
}
так будет на 10-20% быстрее
Все дело в том что на ассемблере у тебя генерится сравнение в виде операции cmp которая автоматически генерит флаг состояния по все видам сравнения (> < и ТД и та) и потом генерится код с необходимым джампом. Если по моему то просто проверка на (не ноль) при флаге -О3.
Есть ещё более извратный вариант:
for(int i=big_num/4; i; --i){
/*do your stuff 1*/
/*do your stuff 2*/
/*do your stuff 3*/
/*do your stuff 4*/
}
for(int i=big_num%4; i; --i){
/*do your stuff*/
}
итоговая производительность должна возрасти в 2раза минимум
Насчёт ситайпс - сделай.
Развлекайся)))
"итоговая производительность должна возрасти в 2раза минимум"
Итоговая производительность в первую очередь зависит от тела цикла. В большинстве реальных задач затраты на итерирование на порядки меньше затрат на выполнение тела цикла. Первый вариант, с обратным итерированием в реальности будет применим далеко не всегда. Про второй лучше вообще не упоминать.
Реально крутые видосы
Смотрю с удовольствием
Как сказал ещё mCoding:
Самый быстрый способ писать циклы в питоне - не писать циклы в питоне
Если вам нужна скорость в питоне - не используйте питон
Неоднозначное видео. Автор, естественно, не врёт, особенно если понять, что во второй функции просто небольшая опечатка. Там нужно убрать у result += num знак плюса, вы получите похожие значения. Т.е. название видео полностью оправданно, при работе с пайтоном нужно помнить, где можно сэкономить время.
Но с другой стороны важно понимать принципиальную разницу двух циклов. В данной ситуации вы просто пытаетесь использовать цикл while взамен цикла for. Однако они имеют разное значение в программировании. While изначально не подразумевает работу с такими большими значениями, и он работает непосредственно с true и false, т.е. входные параметры можно сделать любые, сравнивать хоть текст, хоть логическое, хоть цифру. А цикл с параметром изначально задуман как счётчик. Можно ли заменять эти циклы друг другом? Да. Но зачем? В теории можно ходить на руках и брать ногами вилку. Но есть ли в этом смысл? Не думаю.
Какой плагин используется для такой темы?
Material UI
Извините за тупой вопрос (не давно начал изучать Пайтон), но как поставить стрелочку, в место "->"
привет, подскажи пожалуйста, что это за стрелочки после функции? для чего они нужны и как их писать?
Это называется тайп хинты (type hint). Они нужны чтобы подсказать програмисту и среде разработки какой тип данных возвращает данная функция. Чисто для удобства существует. Записывается:
def my_function() -> :
@@predatel_rodini ну это уже немного дебри. Я объяснил поверхостно и просто
Я уже являюсь евангелистом PyPy. Хожу и всем рассказываю про него и многие не слышали про PyPy. Это версия пайтона написана на пайтоне и к нему прикручен JIT компилятор. Давайте посмотрим. У меня loop1 на обычном пайтоне выполняется за 12.566052499998477, а вот на PyPy за 0.1593255. То есть фактически мгновенно. Для тестов надо запускать файл pypy3.9.exe Я на PyPy как раз делаю такие вещи, где надо чего-то подсчитать, он справляется очень и очень шустро.
Спасибо! Про подключение сишных библиотек очень интересно
если в первом случае в def loop1()
убрать (не нужный) result+=num то скорость сравняется со вторым вариантом, где используется range
более того, если в def loop2()
нагрузить первым бессмысленным действием (result+=num) то и скорость выполениея с range будет примерно такой же как и без range
вывод :
первые два примера имеют разницу в быстродействии аж 13% ( что имхо малосущественно ),
остальные ещё не смотрел,
но по общему мнению(что тоже требует проверки) numpy быстрее обычного питона на порядок, что уже существенно
7:16 - а если компилировать с флагом оптимизации `-O3` весь цикл сворачивается в формулу. Цикл на 1е15 итераций отрабатывает за 3мс (время запуска, выполнения и завершения программы с выводом результата в консоль). Куда интересней просуммировать последовательность, как в python `sum(range(10**8))`, т.е. `sum += num` а не `sum++`.
Такое `-O3` тоже свернет в num * (value * (value + 1)) >> 1. Автор, изучи, пожалуйста, опции оптимизации gcc прежде чем бенчмаркать! Используемый метод time имеет точность в секунду, вы не могли не заметить этого, так что замеры там практически бесполезны, можно использовать clock(), в C++ есть вариант получше -- std::chrono
Настоящая интеграция с C - это Pytex/Cython. Питоновский код транслируется в C и компилируется gcc, а синтаксическое надмножество Pyrex имеет плюшки вроде статической типизации, структур и указателей.
Привет, спасибо за видео. Используешь ли пайчарм? Стоит ли к нему привыкать заранее даже тогда, когда весь его функционал не нужен, или считаешь лучше на него пересаживаться только по надобности?
Использую. Тут кому как, надо отталкиваться от потребностей, некоторым и на виме норм
Все очень интересно, но ничего не ясно.
Так по видео же сравнивается арифмитические операции суммировани, а не сами циклы как таковые.
Допустим какая польза мне от sum() если мне нужно перебрать словарь?
А вот видос как прикрутить C к python для ускорения будет полезно
Я не силен в программировании, но вроде как функции loop1 и loop2 вернут разные значения. О каком сравнении идет речь?
И какой линукс используется?
Чтобы посмотреть время работы программы, можно запускать так: time ./main. И тогда не нужно это реализовывать в коде.
Ну да, но там насколько я знаю не выводится время каждой функции, хотя возможно и ошибаюсь, так как давно не делал подобное
@@zproger Да, выводит время работы программы. Но если программа состоит из одной функции, то получится то, что надо.
Программы не совсем идентичные. В программе на С оптимизатор видит что result никак не используется никуда не передаётся и просто не запускает цикл, вообще все что связано с result в скомпилированный код не попадает, поэтому программа выполняется действительно мгновенно.
это очень круто ! спасибо за видео
😉
Если посмотреть итоговое значение, которое выдаётся при использовании цикла с модулем Numpy, то оно отличается от того, что выдают первоначальные (имеются ввиду циклы в которых рассматривается именно арифметическая прогрессия), если понизить число итераций со 100000000 до 10000, то всё будет работать так как задумывалось автором изначально. Такие вещи нужно проверять на практике, а не слепо верить, что показанный вам материал в подобных видео, будет работать как часы.
Точно, по умолчанию он считает элементы int32 поэтому у него будет происходить переполнение НО при этом никаких ошибок не выходит. Чтоб правильно считалось нужно записать так numpy.arange(100_000_000, dtype=numpy.int64) время при этом практически не меняется. Но это не единственная проблема, смотри мой самый свежий комментарий.
Почитал новости, оказывается, началась работа над ускорением CPython. Версия 3.11 будет в разных случаях от 10% до 60% быстрее, чем 3.10. Что было сделано: 1) Faster Runtime. 2) Inlined Python function calls 3) Specializing Adaptive Interpreter Собираются в течение пару лет прикрутить JIT, что уже сделано на PyPy
Измерения через функцию time() в С или C++ некорректны, т.к. точность этого метода только лишь до секунды. Нужно использовать более точные функции, а не time(). Т.к. у Вас какая-то linux образная ОС, то можно было бы использовать clock_gettime(). Ну либо стандартную clock() уже... А то, что у Вас получалось либо 0 либо 1 секунда, то это лишь потому, что момент запуска совпадал с переходом с предыдущей секунды на следующую...
Хороший контент👍 Спасибо!
😉
Глубокомысленно, хорошо, интересно
Спасибо
Замеры скорости работы функции в С вызывает большие сомнения, неужели она выполняется ровно 1 сек или 2 сек с точностю до 6 знака? Там ведь что-то точно округляется, и невозможно сравнить скорость с суммой из Numpy. А в целом, я думаю было бы круто ещё сравнить с Just-In-Time compilation из библиотеки Numba.
Да, в C можно сделать так:
#include
#include
clock_t begin, end;
double elapsed;
begin = clock();
/* Код, время выполнения которого мы хотим замерить */
end = clock();
elapsed = (double)(end - begin) / CLOCKS_PER_SEC;
printf("Elapsed %lf sec
", elapsed);
Видео крутое и очень информативное! Только почему нельзя было вывести точное время выполнения в C, а не округлять?
он ничего не округлял, time_t имеет точность в секунду, поэтому никаких мс быть не может. Обычно для замеров времени выполнения программы в Linux ее запускают с помощью утилиты time, тогда точность совсем другая. Но тут проблема совсем не в точности замера, а в непонимании работы компилятора. Подробнее я написал тут в коментарии
А теперь давайте представим, что есть такая библиотека, как numba и попробуем воспользоваться её функцией njit, в роли декоратора.
import timeit
import numpy as np
from numba import njit
value = 100_000_000
def loop1(num=0, result=0) -> int:
while num < value:
result += num
num += 1
return result
def loop2(result=0) -> int:
for num in range(value):
result += num
return result
def loop3() -> int:
return sum((num for num in range(value)))
def loop4() -> int:
return sum([num for num in range(value)])
def loop5() -> int:
return sum(range(value))
def loop6() -> int:
return np.sum(np.arange(value))
@njit
def loop7(num=0, result=0) -> int:
while num < value:
result += num
num += 1
return result
@njit
def loop8(result=0) -> int:
for num in range(value):
result += num
return result
print(f"loop1: {timeit.timeit(loop1, number=1)}")
print(f"loop2: {timeit.timeit(loop2, number=1)}")
print(f"loop3: {timeit.timeit(loop3, number=1)}")
print(f"loop4: {timeit.timeit(loop4, number=1)}")
print(f"loop5: {timeit.timeit(loop5, number=1)}")
print(f"loop6: {timeit.timeit(loop6, number=1)}")
print(f"loop7: {timeit.timeit(loop7, number=1)}")
print(f"loop8: {timeit.timeit(loop8, number=1)}")
Для чего стрелочки "-> int"в функции loop? Без них функция будет работать по-другому?
Аннотация, указывающая на тип данных который должна возвращать функция. Нужна в основном просто для того, чтобы лучше ориентироваться в коде. Работа функции от этого не меняется
по-моему, он просто явно задал тип возвращаемого значения. Функция так же работает.
Это аннотация типа для линтеров. Она никак не влияет на компилятор, он просто проигнорирует эту часть строки. Зато, если подключены линтеры, они смогу выдать предупреждение о несоответствии типа возвращаемого значения с ожидаемым.
Маленькое дополнение по NumPy после моих экспериментов с ним:
1. У автора вычисляется НЕ верная сумма, т.к. он использовал arange по умолчанию, а по умолчанию он считает элементы int32 поэтому у него будет происходить переполнение НО при этом никаких ошибок не выходит. Чтоб правильно считалось нужно записать так numpy.arange(100_000_000, dtype=numpy.int64) время при этом практически не меняется.
2. У всего есть цена и у arange она тоже есть, и это то что в отличии от range он СОЗДАЕТ ВЕСЬ массив в памяти, т.е. все эти 100 миллионов элементов! Когда я попытался создать больший массив на 10 миллиардов он вывел ошибку что памяти не хватает и нужно 74Гб оперативки!!! Кстати 70% времени работы уходит на создание этого массива, само суммирование за остальные 30%. Т.е если бы не создавался массив, можно было бы в 2 раза еще ускорить, но я не знаю возможно ли это в NumPy,. Это точно не numpy.sum т.к. он ждет на вход только массив.
уточнение: numpy.sum берёт объект range() и память при этом не захватывает, но и работает примерно тоже время что цикл for, т.е. в 30 раз медленнее.
NumPy в данном случае не подходит, а вот использование numba даёт просто колоссальное ускорение. Тот же цикл ускорился так что не фиксируется таймером, пришлось поставить 4_000_000_000 и summa считать во float, только тогда время выполнения стало 0.3 секунды. Память вообще не расходуется.
Так или иначе полезная инфа
Помню о numpy с ML, но в таких скоростях нет необходимости)
спасибо интересно.
сравнил у себя
Python 3
loop1 - 7.2
loop2 - 4.5
Python 2.7.18
loop1 - 5.5
loop2 - 9.8
loop2 - 8.2 (если вместо range использовать xrange)
IronPython 2.7.3
loop1 - 4
loop2 - 9 (если вместо range использовать xrange)
Сравнил с PyPy, у меня первый цикл выполняется практически мгновенно loop1: 0.1593255. Ведь "PyPy is 4.5 times faster than CPython"
Хочу ctype! ) и если можно написание библиотек на Си для питона?
Поигрался с циклами, действительно Пи очень медленный, и на нем еще написано большинство современного софта ( ( .. Придется С вспоминать) Спасибо за gcc
Интересно видео 100% нигде его не видел:)
Мало кто знает, но самый быстрый способ выполнить программу в питоне - это не использовать питон, а перейти на что-то более менее адекватное.
🤔
Привет, как обновить питон в Zorin OS 16?
Спасибо автору, всегда ненавидел питон, любил С++, теперь понял что ошибался, видно грамотного человека
Нет ничего плохого использовать сразу 2 этих языка, я активно использую плюсы в тандеме с пайтоном. На пайтоне пишу простую логику где не нужны какие-то сложные вычисления, а на плюсах наоборот пишу всю сложную логику где важна скорость вычисления
Отличное видео👍 Жду Numpy
😉
а на ассемблере будет быстрее?
Да
Интересно
Вообще все делаю с лист компрехеншен, генератор, не залупит ничего в память.
Поиск по файлу займёт время, но не так долго)
Зависит от количества данных.
Лист комп конечно не прямо миг, но я и не МЛ специалист тоже)
Си конечно мощно. Но проще всё же на Rust ускорить пайтон.
За два года коммерческой разработки бекенда, ни разу не потребовались мощные вычисления в коде. Всё до боли банально - достать из БД или положить/обновить в БД запись.
Хотя. Если в главном сервисе делать агрегацию данных, т.е. полученные по шине данные из разных сервисов сложить в один json, то наверное можно и написать на Си/Раст.
П.С. Была задача по расчету количества потомков у животного до 4 колена. Но это нереально делать в коде, т.к. 5млн записей в БД занимает много места. Получить запросом и обработать не выйдет. Тут опять же нужно на SQL делать.
В общем, непонятно где может пригодиться.
Спасибо за твои видосы, да было бы хорошо посмотреть про библеотеку numpy
Самый лучший способ оптимизировать pyton программу-написать ее на С++
🤔
Круто!
😉
В си (или си++) можно реализовать блок через ассемблер и будет ещё быстрей😀
это да)
расскажи про cython и формат файлов .pyx
Тыщу лайков, хмм... ну лан лайкану, мне понравился хинт что в питоше можно int записать как 100_000_000.. Я не очень знаю питон, но отлично знаю Си. По поводу numpi скажу, что стоило-бы проверить ещё ударную по потокам процессора... Сисадмины очень часто жалуются на эту либу что она глушит процак. Итерация с сложением в Си скорее не милисикунды, скорее всего это наносекунды. В конечном итоге это скорее всего 8 команд асемблера, что для процессора вообще раз плюнуть.
Благодарю, полезная информация
Дядь сделай пример со строками, например когда ты их парсишь и формируешь обьект, а не вот эта математика, которую питонисты увидят раз в жизни (в универе) а не на работе
Круто!)
Не люблю писать коментарии, но твои видео просто восхитительны, знай что ты это делаешь очень круто и без воды, годный видос )
Благодарю!
Жаль, что можно поставить только один лайк 👍
Действительно жаль =)
Спасибо
Очень полезно
Благодарю!
Что значит "-> int:" в функции
А ещё есть вопрос, зачем нужно было писать число так: "100_000_000" , если можно 1000000000
1. Аннотация, указывающая на тип данных который должна возвращать функция. Нужна в основном просто для того, чтобы лучше ориентироваться в коде. Работа функции от этого не меняется
2. Чтобы сразу было видно, что это сто миллионов, без необходимости лишний раз считать нули. Но записывать можно как угодно
Это аннотация, есть видео об этом на канале.
По-поводу другого вопроса, пишу "100_000_000" потому что
так легче понять какое число записано.
Спасибо за ответ, обзятельно посмотрю видео
вот прям спасибо!
😉
Python написали что бы не писать на С, а давайте писать на С - Python медленный 😅
Ты прикола ради си всегда компилируешь без оптимизаций, чтобы он был раз в 10 медленнее чем может быть?
с примером на С не совсем верно - С- код скомпилил, а пайтон - интерпретатор...
Но всё же важные компоненты можно писать на нем для интеграции в Python, подобным образом делают и в Java, тоже внедряют нативку
@@zproger понятное дело, но тут было сравнение скорости компилятора и интерпретатора что в корне неверно.
Что касаемо внедрения кода, еще в Турбо Паскале можно было внедрять ассемблерные блоки...
И всё же, говорить что Python медленнее C - не верно, язык не может быть быстрым или медленным. Другой вопрос - имплементация решений. В решении на С не делается дополнительных проверок, копирований и т.п.
Го видос про numpy!!!!
Попробовал с Go (просто цикл, без какой либо оптимизации)
100 000 000 - 42 ms
1 000 000 000 - 318 ms
В PyPy первый луп ещё быстрее 15 ms
Не совсем корректно говорить что Си работает быстрей чем питон в данном контексте. Быстрей здесь работает то, что меньше затрачивает обращений к аллокатору. Тот же си если будет постоянно малочить, будет медленней чем си работающий со стеком. И питон, который дофига создаёт объектов для gc, работает медленней, чем питон, который задействует меньше объектов (скажем встроенные функции) и не создаёт лишние обращения к памяти и трековые ссылки… и скорее речь здесь стоит вести об оптимизации работы с памятью, а не сравнивать два языка имхо…
А, чтобы Пито работал быстрее нам нужно использовать С… ок
Невероятно крутой видос
может тогда в с++ кое-что писать на python
Можно и так :D
Если серьёзно, то видео - просто ерунда какая-то. Циклы предназначены, чтобы многократно выполнять наборы инструкций а не для подсчёта суммы.
Для подсчёта суммы лучше и быстрее будет воспользоваться функцией sum, как раз таки.
Python действительно медленный, особенно если не знаешь, что делаешь, для чего и каким инструментом для этого следует пользоваться.
...В первом цикле прибавляет 1, а во втором num... Конечно второй цикл будет больше, гений
Где-то я видел это видео)
😉
++i работает быстрее i++, ошибка получается
Какой же это "стандартный" цикл в Python, если интерпретатор его запускает в виде либы ?
это все не интересно числа числа , ускорить простой код было бы приятней
Так и сделаю в след.видео, спасибо
Итерацью на префиксную, не происходит копирования переменной.
теперь понятно как комбинировать 2 языка)
Это да =)
кроме numpy ещё numba увеличивает скорость :)
Это да, но для неё отдельное видео =)
Видео о ctypes нужно.
Ок, сделаю =)
Python не написан на Си, не вводите людей в заблуждение. Это язык, и у него есть имплементации, большинство на Си, но есть и другие. Jython и IronPython, например.
да ужж сравнили 2ное вычисление с одинарным), и подход нелогичный немног, тогда уж не си , а голанд дучше бы импортировали, теперь уже даж для игрушек берут многопроцессорные сокеты и процы по 20 ядер с куевым количеством потоков и язык си уже стал подтупливать, а питон в принцыпе не для быстрых задач, а для всех кроме десктопа почти)
8:35 если ты можешь написать все на языке C нафига тебе Пайтон???
на питоне скорость разработки выше
Мало того что сами функции не идентичны, в таймите прогнал 1 раз, да ещё и sum(range(n)) называет циклом, хотя это, мягко говоря, строка кода под одну единственную задачу. Подача материала "со знанием дела"...
Тот же mCoding яснее и правильнее все объяснил, пускай и без загрузки С кода.
Прикол в том, что на практике это малок кому вообще пригодится. По этому нет никакого смысла учить С, когда есть Пайтон.
И да, если уж очень надо будет, то можно будет посмотреть это видео и сделать нужную функцию.