Как уменьшить время выполнения программы в python
Перейти к содержимому

Как уменьшить время выполнения программы в python

  • автор:

Как уменьшить время работы кода?

Имеется обученная модель, на основе которой необходимо проставить категорию текста из файла и записать данную категорию в другой файл. В файле более 20 000 строк.

for schetchik in range(list): text = descriptions[schetchik] tokenizer = Tokenizer() tokenizer = Tokenizer(num_words=num_words) tokenizer.fit_on_texts(descriptions) tokenizer.word_index sequences = tokenizer.texts_to_sequences(descriptions) sequence = tokenizer.texts_to_sequences([text]) data = pad_sequences(sequence, maxlen=max_review_len) result = model.predict(data) prediction = model.predict(np.array(data)) predicted_label = text_labels[np.argmax(prediction)] with open('PREDICT.csv', 'r+') as f: f.seek(0, 2) f.write(str(predicted_label)+"\n") print(text) print(predicted_label) 

Отслеживать

13.7k 12 12 золотых знаков 43 43 серебряных знака 75 75 бронзовых знаков

Ускорение кода на Python средствами самого языка

Каким бы хорошим не был Python, есть у него проблема известная все разработчикам — скорость. На эту тему было написано множество статей, в том числе и на Хабре.

  • Использовать Psyco
  • Переписать часть программы на С используя Python C Extensions
  • Сменить мозгиалгоритм
  1. «Порог вхождения» у C и Python/C API все же выше, чем у «голого» Python’a, что отсекает эту возможность для разработчиков, не знакомых с C
  2. Одной из ключевых особенностей Python является скорость разработки. Написание части программы на Си снижает ее, пропорционально части переписанного в Си кода к всей программе
Так что же делать?

Тогда, если для вашего проекта выше перечисленные методы не подошли, что делать? Менять Python на другой язык? Нет, сдаваться нельзя. Будем оптимизировать сам код. Примеры будут взяты из программы, строящей множество Мандельброта заданного размера с заданным числом итераций.
Время работы исходной версии при параметрах 600*600 пикселей, 100 итераций составляло 3.07 сек, эту величину мы возьмем за 100%

Скажу заранее, часть оптимизаций приведет к тому, что код станет менее pythonic, да простят меня адепты python-way.

Шаг 0. Вынос основного кода программы в отдельную

Данный шаг помогает интерпретатору python лучше проводить внутренние оптимизации про запуске, да и при использовании psyco данный шаг может сильно помочь, т.к. psyco оптимизирует лишь функции, не затрагивая основное тело программы.
Если раньше рассчетная часть исходной программы выглядела так:

for Y in xrange(height): for X in xrange(width): #проверка вхождения точки (X,Y) в множество Мандельброта, itt итераций 

То, изменив её на:

def mandelbrot(height, itt, width): for Y in xrange(height): for X in xrange(width): #проверка вхождения точки (X,Y) в множество Мандельброта, itt итераций mandelbrot(height, itt, width) 

мы получили время 2.4 сек, т.е. 78% от исходного.

Шаг 1. Профилирование

Стандартная библиотека Python’a, это просто клондайк полезнейших модулей. Сейчас нас интересует модуль cProfile, благодаря которому, профилирование кода становится простым и, даже, интересным занятием.
Полную документацию по этому модулю можно найти здесь, нам же хватит пары простых команд.

python -m cProfile sample.py
Ключ интерпетатора -m позволяет запускать модули как отдельные программы, если сам модуль предоставляет такую возможность.
Результатом этой команды будет получение «профиля» программы — таблицы, вида
4613944 function calls (4613943 primitive calls) in 2.818 seconds

Ordered by: internal time

ncalls tottime percall cumtime percall filename:lineno(function)
1 2.309 2.309 2.766 2.766 mand_slow.py:22(mandelbrot)
.

С её помощью, легко определить места, требующие оптимизации (строки с наибольшими значениями ncalls (кол-во вызовов функции), tottime и percall (время работы всех вызовов данной функции и каждого отдельного соответственно)).

Для удобства можно добавить ключ -s time , отсортировав вывод профилировщика по времени выполнения.

В моем случае интересной частью вывода было (время выполнение отличается от указанного выше, т.к. профилировщик добавляет свой «оверхед»):
4613944 function calls (4613943 primitive calls) in 2.818 seconds

Ordered by: internal time

ncalls tottime percall cumtime percall filename:lineno(function)
1 2.309 2.309 2.766 2.766 mand_slow.py:22(mandelbrot)
3533224 0.296 0.000 0.296 0.000
360000 0.081 0.000 0.081 0.000
360000 0.044 0.000 0.044 0.000
360000 0.036 0.000 0.036 0.000
.
Итак, профиль получен, теперь займемся оптимизацией вплотную.

Шаг 2. Анализ профиля

Видим, что на первом месте по времени стоит наша основная функция mandelbrot, за ней идет системная функция abs, за ней несколько функций из модуля math, далее — одиночные вызовы функций, с минимальными временными затратами, они нам не интересны.

Итак, системные функции, «вылизаные» сообществом, нам врядли удастся улучшить, так что перейдем к нашему собственному коду:

Шаг 3. Математика

Сейчас, код выглядит так:

pix = img.load() #загрузим массив пикселей def mandelbrot(height, itt, width): step_x = (2 - width / 1.29) / (width / 2.6) - (1 - width / 1.29) / (width / 2.6) #шаг по оси х for Y in xrange(height): y = (Y - height / 2) / (width / 2.6) #для Y рассчет шага не так критичен как для Х, его отсутствие положительно повлияет на точность x = - (width / 1.29) / (width / 2.6) for X in xrange(width): x += step_x z = complex(x, y) phi = math.atan2(y, x - 0.25) p = math.sqrt((x - 0.25) ** 2 + y ** 2) pc = 0.5 - 0.5 * math.cos(phi) if p 2: color = (i * 255) // itt pix[X, Y] = (color, color, color) break else: pix[X, Y] = (255, 255, 255) print("\r%d/%d" % (Y, height)), 

Заметим, что оператор возведения в степень ** — довольно «общий», нам же необходимо лишь возведение во вторую степень, т.е. все конструкции вида x**2 можно заменить на х*х, выиграв таким образом еще немного времени. Посмотрим на время:
1.9 сек, или 62% изначального времени, достигнуто простой заменой двух строк:

p = math.sqrt((x - 0.25) ** 2 + y ** 2) . Z_i = Z_i **2 + z 
p = math.sqrt((x - 0.25) * (x - 0.25) + y * y) . Z_i = Z_i * Z_i + z 
Шажки 5, 6 и 7. Маленькие, но важные

Прописная истина, о которой знают все программисты на Python — работа с глобальными переменными медленней работы с локальными. Но часто забывается факт, что это верно не только для переменных но и вообще для всех объектов. В коде функции идут вызовы нескольких функций из модуля math. Так почему бы не импортировать их в самой функции? Сделано:

def mandelbrot(height, itt, width): from math import atan2, cos, sqrt pix = img.load() #загрузим массив пикселей 

Еще 0.1сек отвоевано.
Вспомним, что abs(x) вернет число типа float. Так что и сравнивать его стоит с float а не int:

if abs(Z_i) > 2: ------> if abs(Z_i) > 2.0: 

Еще 0.15сек. 53% от начального времени.

И, наконец, грязный хак.
В конкретно этой задаче, можно понять, что нижняя половина изображения, равна верхней, т.о. число вычислений можно сократить вдвое, получив в итоге 0.84сек или 27% от исходного времени.

Заключение

Профилируйте. Используйте timeit. Оптимизируйте. Python — мощный язык, и программы на нем будут работать со скоростью, пропорциональной вашему желанию разобраться и все отполировать:)
Цель данной статьи, показать, что за счет мелких и незначительных изменения, таких как замен ** на *, можно заставить зеленого змея ползать до двух раз быстрее, без применения тяжелой артиллерии в виде Си, или шаманств psyco.
Также, можно совместить разные средства, такие как вышеуказанные оптимизации и модуль psyco, хуже не станет:)

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

UPD Полезную ссылку в коментариях привел funca.

  • python
  • python c api
  • python speed
  • performance optimization
  • оптимизация кода
  • python 2.7

7 простых способов оптимизировать код Python

Python использует хеш-таблицы для управления множествами. Всякий раз, когда мы добавляем элемент в множество, интерпретатор Python определяет его позицию в памяти, выделенной для множества, используя хэш целевого элемента.

Поскольку Python автоматически изменяет размер хеш-таблицы, скорость будет постоянной (O (1)) вне зависимости от размера набора. Именно это ускоряет выполнение операций.

В Python операции с множествами включают объединение, пересечение и разность. Поэтому вы можете попробовать использовать их в своем коде – там, где это возможно. Обычно эти операции работают быстрее, чем итерации по спискам.

2. Избегайте использования глобальных переменных.

Это не касается не только Python, почти все языки не одобряют чрезмерное или незапланированное использование глобальных переменных. Причина в том, что у них могут быть скрытые / неочевидные побочные эффекты, ведущие к коду Спагетти. Плюс ео всему, Python очень медленный при доступе к внешним переменным.

Это вынуждает ограничить использование глобальных переменных. Можно объявить внешнюю переменную, используя ключевое слово global (что тоже не всегда правильно ��).
Кроме того, лучше сделать локальную копию, прежде чем использовать глобальные переменные внутри циклов.

3. Использование внешних библиотек / пакетов.

Некоторые библиотеки python имеют эквивалент «C» с теми же функциями, что и исходная библиотека. Будучи написаны на “C”, они работают быстрее. Например, попробуйте использовать cPickle вместо использования pickle.

Можно использовать , который является оптимизирующим статическим компилятором для обоих Python. Это расширение Python, поддерживающее функции и типы C. Код на нем выполняется быстрее и эффективнее.

Также можно использовать пакет PyPy. Он включает компилятор JIT (Just-in-time), который делает код Python невероятно быстрым. PyPy можно дополнительно настроить для еще большего повышения производительности.

4. Используйте встроенные модули и функции.

Python является интерпретируемым языком и основан на абстракциях высокого уровня. Поэтому лучше использовать встроенные модули везде, где это возможно. Это сделает код более эффективным, поскольку встроенные модули предварительно компилируются и выполняются быстро. В то время как длительные итерации, которые включают интерпретируемые шаги, выполняются очень медленно.

Аналогичным образом, предпочтительно использовать встроенные функции, такие как, например, map, которые значительно ускоряют код

5. Ограничьте поиск в методе с использованием цикла

При работе в цикле нужно кэшировать вызов метода, а не вызывать метод для объекта. В противном случае поиск получится дороговатым.

Просто взгляните на этот пример, и сразу станет понятно, о чем речь.

6. Оптимизация использования строк.

Конкатенация строк идет медленно, никогда не делайте это внутри цикла. Вместо этого используйте метод join. Или используйте функцию форматирования для формирования унифицированной строки.

Операции RegEx в Python выполняются быстро, поскольку они в конечном итоге приходят к C-коду. Однако, в некоторых случаях, основные методы строки, такие как , работают лучше.

Кроме того, можно проверить различные методы, используя модуль . Это поможет определить, какой метод действительно самый быстрый.

7. Оптимизация при помощи оператора if.

Как и в большинстве языков программирования, в Python есть “ленивая” оценка. Это означает, что если есть цепочка условий «and», то проверка остановится на первом ложном условии.

Это поведение Python можно использовать в целях опитмизации кода . Например, если вы ищете фиксированный шаблон в списке, можно уменьшить область поиска, добавив условие «and», которое становится ложным, если размер целевой строки меньше длины шаблона.

Также можно сначала проверить быстрое условие (если оно есть), например «строка должна начинаться с @», или «строка должна заканчиваться точкой».

Приёмы для ускорения кода на Python

Для ускорения кода на Python программисты могут использовать много приемов. Мы собрали несколько самых простых и при этом самых эффективных из них.

Python – один из самых популярных языков программирования в мире. Этим он обязан своему простому синтаксису и богатой экосистеме. В последнее время он используется в соревновательном программировании, где большое значение имеет скорость выполнения программ.

Большинство из наших читателей, вероятно, уже начали писать на Python. Сперва всё кажется простым и очевидным. Но при решении задач со сложными алгоритмами начинается головная боль с Time Limit Exceeded . Однако, в этом нет вины Python – это вина программиста. Да, Python медленный, но если программист напишет эффективную программу, она точно выполнится без подобных загвоздок.

Представляем вам несколько приемов и подходов для ускорения кода и повышения его эффективности.

Используйте подходящие структуры данных

Применение правильных структур данных значительно ускоряет выполнение кода.

В Python встроены такие структуры данных, как список ( list ), кортеж ( tuple ), множество ( set ) и словарь ( dictionary ). Несмотря на это, большинство людей хорошо помнят только про списки. Это неправильный подход.

Для ускорения кода используйте те структуры данных, которые максимально соответствуют вашей задаче. Особенно это касается выбора между списком и кортежем, ведь итерирование по последнему занимает куда меньше времени.

Избегайте циклов for

В случаях, когда цикл for обрабатывает диапазон непостоянного размера, его выполнение в Python происходит медленнее, чем выполнение цикла while . Поэтому в таких случаях лучше прибегайте к while .

Применяйте списковые включения (list comprehension)

Не обращайтесь ни к какой другой технике, если можно использовать списковые включения. Например, этот код заносит в список все числа между 1 и 1000, кратные 3:

L = [] for i in range (1, 1000): if i%3 == 0: L.append (i)

Со списковыми включениями код трансформируется в одну строку:

L = [i for i in range (1, 1000) if i%3 == 0]

Этот приём работает быстрее, чем просто метод append() .

Не пренебрегайте множественным присваиванием

Не стоит инициализировать несколько переменных так:

a = 2 b = 3 c = 5 d = 7

Лучше придерживайтесь следующего синтаксиса:

a, b, c, d = 2, 3, 5, 7

[python_ad_block]

Не создавайте глобальные переменные

Да, в Python есть ключевое слово global для объявления таких переменных. Но операции с ними требуют больше времени, чем с локальными. Потому не создавайте глобальные переменные без крайней необходимости.

Применяйте библиотечные функции

Не пишите функцию вручную, если она уже реализована в какой-нибудь библиотеке. Библиотечные функции крайне эффективны, и, скорее всего, вам не удастся достичь лучшего результата самостоятельно.

Соединяйте строки методом join

В Python конкатенацию строк можно производить при помощи знака + .

concatenatedString = "Программирование " + "это " + "весело."

Но также для этого есть метод join() .

concatenatedString = " ".join (["Программирование", "это", "весело."])

Всё дело в том, что оператор + каждый раз создаёт новую строку, а затем копирует в неё исходные. join() устроен иначе и обеспечивает выигрыш во времени.

Используйте генераторы

Если у вас в списке хранится много данных, которые требуется использовать все за раз, применяйте generator . Это сэкономит ваше время.

Будьте бдительны

Взгляните на следующий код:

L = [] for element in set(L): .

Данный код может показаться эффективным, так как в нём для удаления дубликатов используется set . Но на самом деле он будет выполняться долго. Не забывайте, что приведение списка ко множеству – это время. Так что этот вариант будет лучше:

for element in L: .

Избегайте точек

Старайтесь не пользоваться ими. Взгляните на пример:

import math val = math.sqrt(60)

Вместо этого можно применить следующий синтаксис:

from math import sqrt val = sqrt(60)

Всё потому, что когда вы вызываете функцию с помощью точки, она сперва обращается к методу __getattribute()__ или __getattr()__ . Эти методы, в свою очередь, используют операции со словарями, отнимающие время. Поэтому старайтесь писать: from module import function .

Используйте 1 в бесконечных циклах

Пишите while 1 вместо while True . Это выиграет вам немного времени.

Попробуйте другие подходы

Не бойтесь применять новые практики для повышения эффективности кода.

Допустим, у вас есть код:

if a_condition: if another_condition: do_something else: raise exception

Вместо этого стоит попробовать:

if (not a_condition) or (not another_condition): raise exception do_something

Используйте ускорители

Медлительность Python послужила вдохновением для различных проектов, сокращающих его время работы. На большинстве соревнований по программированию вы встретитесь с pypy (там, где можно писать на Python).

Эти средства помогут уменьшить время выполнения Python-программ.

Для больших датасетов используйте специальные библиотеки

C/C++ быстрее Python. Поэтому многие пакеты и модули, которые можно использовать в программах на Python, пишутся на C/C++. Среди таких модулей – Numpy, Scipy и Pandas, столь необходимые при обработке больших массивов данных.

Используйте последнюю версию Python

Python регулярно обновляется и совершенствуется и с каждым релизом становится всё быстрее и оптимизированнее. Поэтому для ускорения кода всегда пишите его на новейшей версии языка.

Заключение

Мы рассмотрели приёмы для ускорения кода на Python. Конечно, этот список не исчерпывающий: есть и другие способы, которые могут вам пригодиться. Обязательно ищите их и пишите код эффективно!

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *