6 способов измерить скорость программы на Python
Недавно мы решали сложную задачу Эйнштейна с помощью кода на Python, а потом оптимизировали его, чтобы сократить время выполнения. Там всё было просто: с четырёх часов мы оптимизировали время выполнения до долей секунды, и это было явно заметно. Но бывает так, что даже полсекунды оптимизации — это очень хорошо, когда речь идёт о высоконагруженных сервисах. Например, в соцсетях, которыми пользуются сотни тысяч пользователей в минуту. Сегодня мы покажем целых 6 простых способов измерения времени работы кода, которые может использовать каждый.
Как измерять время выполнения кода
В большинстве случаев измерить время работы кода можно так:
- Зафиксировать время начала работы.
- Зафиксировать время окончания работы.
- Вычесть первое значение из второго.
Ещё важно измерять время выполнения кода при одних и тех же условиях:
- конфигурация и мощность компьютера должны совпадать для всех замеров;
- загрузка процессора должна быть одинаковой;
- программа для работы с кодом должна быть одной и той же с одинаковой версией.
Но даже если эти условия выполняются, результаты нескольких замеров могут немного различаться для одного и того же кода. На это могут влиять фоновые процессы, поэтому для точных измерений проводят несколько замеров в чистой среде, когда кроме кода и системных процессов ничего нет.
Если будете заниматься оптимизацией, вот вам на вырост: нужно различать понятия wall time («время на стене») и процессорное время. Первое показывает прошедшее время от начала до конца работы, второе — время, которое процессор затратил на выполнение кода. Их значения могут различаться, если программа ожидает высвобождение ресурсов для выполнения. Но сейчас можно без этих тонкостей.
Модуль datetime
С помощью такого способа можно измерить время выполнения кода в формате часы:минуты:секунды:микросекунды. Мы использовали модуль datetime, когда оптимизировали код для решения загадки Эйнштейна и ускоряли работу программы более чем в 200 тысяч раз.
# подключаем модуль datetime import datetime # фиксируем и выводим время старта работы кода start = datetime.datetime.now() print('Время старта: ' + str(start)) # код, время работы которого измеряем #фиксируем и выводим время окончания работы кода finish = datetime.datetime.now() print('Время окончания: ' + str(finish)) # вычитаем время старта из времени окончания print('Время работы: ' + str(finish - start))
Результат — 51 тысячная секунды. Неплохо, но что покажут другие способы?
Модуль time
Модуль time предоставляет разные возможности для измерения времени работы кода:
- time.time() поможет измерить время работы в секундах. Если нужно получить время в минутах, результат вычисления нужно разделить на 60, в миллисекундах — умножить на 1000.
- time.perf_counter() также можно использовать для измерения времени в секундах, но таймер не будет зависеть от системных часов. Функцию используют, чтобы избежать погрешностей. Функция time.perf_counter_ns() вернёт значение в наносекундах.
- time.monotonic() подходит для больших программ, поскольку эта функция не зависит от корректировки времени системы. Функция использует отдельный таймер, как и time.perf_counter(), но имеет более низкое разрешение. С помощью time.monotonic_ns() можно получить результат в наносекундах.
- time.process_time() поможет получить сумму системного и пользовательского процессорного времени в секундах, не включая время сна. Если процесс выполнения блокируется функцией time.sleep() или приостанавливается операционной системой, это время не включается в отчётное. Для наносекунд есть функция time.process_time_ns(), но её поддерживают не все платформы.
- time.thread_time() сообщит время выполнения текущего потока, а не процесса. Если в коде есть функция time.sleep(), время её выполнения не будет включено.
Time.time(). Давайте посчитаем время выполнения нашего кода с помощью функции time.time() в миллисекундах:
# подключаем модуль time import time # фиксируем время старта работы кода start = time.time() # код, время работы которого измеряем #фиксируем время окончания работы кода finish = time.time() # вычитаем время старта из времени окончания и получаем результат в миллисекундах res = finish - start res_msec = res * 1000 print('Время работы в миллисекундах: ', res_msec)
Получаем результат: 61 тысячная секунды. Результат отличается от предыдущего, тут уже нужно было бы хорошо сделать серию тестов и посчитать среднее значение.

Time.perf_counter(). Посчитаем время выполнения нашего кода с помощью функции time.perf_counter() в секундах:
# подключаем модуль time import time # фиксируем время старта работы кода start = time.perf_counter() # код, время работы которого измеряем #фиксируем время окончания работы кода finish = time.perf_counter() # вычитаем время старта из времени окончания и выводим результат print('Время работы: ' + str(finish - start))
Мы получили 51 тысячную секунды — почти такой же результат, как и в самый первый раз. Кажется, что это точное время, но посмотрим, что будет дальше.

Time.monotonic_ns(). Посчитаем время выполнения нашего кода с помощью функции time.monotonic_ns() в наносекундах:
# подключаем модуль time import time # фиксируем время старта работы кода start = time.monotonic_ns() # код, время работы которого измеряем #фиксируем время окончания работы кода finish = time.monotonic_ns() # вычитаем время старта из времени окончания и получаем результат в наносекундах print('Время работы в наносекундах: ' + str(finish - start))
Результат примерно такой же — 52 тысячные секунды, но количество цифр в результате меньше, чем в предыдущем случае.

Time.process_time(). Посчитаем сумму системного и пользовательского процессорного времени в секундах:
# подключаем модуль time import time # фиксируем время старта работы кода start = time.process_time() # код, время работы которого измеряем #фиксируем время окончания работы кода finish = time.process_time() # вычитаем время старта из времени окончания и выводим результат print('Время работы: ' + str(finish - start))
Время работы снова выросло — с 51 до 62 тысячных секунды. Для одних программ такой разброс вообще некритичен, а для других это может означать, что нужно провести больше тестов.

Time.thread_time(). Наконец, посчитаем время выполнения кода с помощью time.thread_time():
# подключаем модуль time import time # фиксируем время старта работы кода start = time.thread_time() # код, время работы которого измеряем #фиксируем время окончания работы кода finish = time.thread_time() # вычитаем время старта из времени окончания и выводим результат print('Время работы: ' + str(finish - start))
Время работы снова выглядит правдоподобно в сравнении с предыдущим результатом.

Что дальше
В следующий раз продолжим оптимизировать наш код решения задачи Эйнштейна: отформатируем и избавимся от вложенных данных. Подпишитесь, чтобы не пропустить продолжение.
Приёмы для ускорения кода на 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. Конечно, этот список не исчерпывающий: есть и другие способы, которые могут вам пригодиться. Обязательно ищите их и пишите код эффективно!
Как ускорить ваш код на Python
Python — это универсальный язык программирования, который широко используется в различных областях, таких как веб-разработка, анализ данных, машинное обучение и искусственный интеллект.
Одной из основных причин его популярности является простота и удобочитаемость, что облегчает разработчикам написание и понимание кода. Однако одним из недостатков Python является его производительность, которая относительно ниже по сравнению с другими языками программирования, такими как C или Java. Поэтому очень важно писать эффективный код для обеспечения оптимальной производительности.
В этой статье мы рассмотрим некоторые советы и рекомендации о том, как писать более качественный и эффективный код на Python. Эти методы могут помочь вам оптимизировать код, сократить время его выполнения и улучшить его общее состояние. Давайте начинать!
1. Использование понимания списков
Понимание списков — это краткий и элегантный способ создания списков в Python. Это сокращенный способ создания списка путем указания элементов, которые он должен содержать, и часто он быстрее и эффективнее, чем использование цикла for для создания списка. Например, рассмотрим следующий код:
squares = [] for i in range(10): squares.append(i**2)
Этот код создает список квадратов чисел от 0 до 9, используя цикл for. Мы можем переписать его, используя понимание списка, следующим образом:
squares = [i**2 for i in range(10)]
Этот код более лаконичен и легче читается, и он делает то же самое, что и предыдущий. Более того, понимание списка происходит быстрее, чем использование цикла for, особенно для больших списков.
2. Использование генераторов вместо списков
В Python генератор — это тип итератора, который генерирует последовательность значений на лету. Генераторы более эффективно используют память, чем списки, потому что они не сохраняют все значения в памяти сразу. Вместо этого они генерируют значения одно за другим по мере необходимости, что может значительно сократить объем памяти, занимаемый вашим кодом. Например, рассмотрим следующий код:
numbers = [1, 2, 3, 4, 5] def square_numbers(numbers): result = [] for number in numbers: result.append(number**2) return result squared_numbers = square_numbers(numbers)
Этот код создает список квадратов чисел в списке numbers, используя цикл for. Мы можем переписать этот код с помощью генератора следующим образом:
numbers = [1, 2, 3, 4, 5] def square_numbers(numbers): for number in numbers: yield number**2 squared_numbers = square_numbers(numbers)
Этот код использует генератор вместо списка для генерации квадратов чисел в списке numbers. Ключевое слово yield используется для генерации каждого значения на лету, что может уменьшить объем памяти, занимаемой кодом. Более того, генераторы часто работают быстрее, чем списки для больших наборов данных.
3. Использование функции enumerate()
Функция enumerate()— это встроенная функция Python, которая позволяет перебирать последовательность (например, список или кортеж) и отслеживать индекс текущего элемента. Функция возвращает кортеж вида (index, value), где index— индекс текущего элемента, а value— значение текущего элемента. Эта функция может быть очень полезна, когда вам нужно перебрать последовательность, а также отслеживать индекс текущего элемента. Например, рассмотрим следующий код:
fruits = [‘apple’, ‘banana’, ‘cherry’, ‘date’] for i in range(len(fruits)): print(i, fruits[i])
Этот код выполняет итерацию по списку fruits и выводит индекс и значение каждого элемента в списке. Мы можем переписать этот код с помощью функции enumerate() следующим образом:
fruits = [‘apple’, ‘banana’, ‘cherry’, ‘date’] for i, fruit in enumerate(fruits): print(i, fruit)
Этот код использует функцию enumerate() для перебора списка fruits и отслеживания индекса и значения каждого элемента. Он более лаконичен и легче читается, чем предыдущий код, но делает то же самое.
4. Использование функции zip()
Функция zip()— это еще одна встроенная функция Python, которая позволяет вам параллельно перебирать несколько последовательностей (таких как списки или кортежи). Функция возвращает кортеж вида (value1, value2, . ), где value1— значение текущего элемента в первой последовательности, value2— значение текущего элемента во второй последовательности и так далее. Эта функция может быть очень полезна, когда вам нужно параллельно перебирать несколько последовательностей и обрабатывать их значения вместе. Рассмотрим следующий код:
names = [‘Alice’, ‘Bob’, ‘Charlie’] ages = [25, 30, 35] for i in range(len(names)): print(names[i], ages[i])
Этот код перебирает списки names и ages, перечисляет их элементы и выводит имя и возраст каждого человека. Мы можем переписать этот код с помощью функции zip() следующим образом:
names = [‘Alice’, ‘Bob’, ‘Charlie’] ages = [25, 30, 35] for name, age in zip(names, ages): print(name, age)
В этом коде используется функцию zip() для параллельного перебора списков names и ages и вывода их значений вместе. Этот код более лаконичен и легче читается, чем предыдущий, но происходит в нём то же самое.
5. Использование оператора in для проверки членства
Оператор in— это встроенный оператор Python, позволяющий проверить, является ли элемент членом последовательности (например, списка или кортежа). Оператор возвращает значение True, если элемент находится в последовательности и False в противном случае. Этот оператор может быть очень полезен, когда вам нужно проверить, находится ли элемент в последовательности перед его обработкой. Например, рассмотрим следующий код:
numbers = [1, 2, 3, 4, 5] if len(numbers) > 0: first_number = numbers[0] else: first_number = None
Этот код проверяет, не пуст ли список numbers, и присваивает переменной first_number значение первого элемента. Мы можем переписать этот код с помощью оператора in следующим образом:
numbers = [1, 2, 3, 4, 5] if numbers: first_number = numbers[0] else: first_number = None
В этом коде используется оператор in, чтобы проверить, не пуст ли список numbers перед его обработкой. Он более лаконичен и легче читается, чем предыдущий код, но делает то же самое.
6. Использование аргументов функций вместо глобальных переменных
В Python обычно рекомендуется использовать аргументы функции вместо глобальных переменных, когда это возможно. Глобальные переменные — это переменные, которые определены вне какой-либо функции и доступны из любой части кода. Аргументы функции — это переменные, которые передаются функции и доступны только внутри функции. Использование аргументов функций вместо глобальных переменных может сделать ваш код более модульным, а также упростить его тестирование и поддержку.
Рассмотрим следующий пример:
total = 0 def add_numbers(numbers): global total for number in numbers: total += number return total result = add_numbers([1, 2, 3, 4, 5]) print(result)
Этот код определяет глобальную переменную total и функцию add_numbers, которая добавляет к переменной значения списка чисел total. Затем функция возвращает значение переменной total. Этот код работает, но у него есть несколько проблем. Во-первых, он использует глобальную переменную, что может быть проблематично в больших программах. Во-вторых, он изменяет глобальную переменную внутри функции, что может затруднить анализ поведения кода.
Мы можем переписать этот код, используя аргументы функции, следующим образом:
def add_numbers(numbers): total = 0 for number in numbers: total += number return total result = add_numbers([1, 2, 3, 4, 5]) print(result)
Этот код определяет функцию add_numbers, которая принимает список чисел в качестве аргумента и возвращает сумму чисел. Этот код более модульный, его легче тестировать и поддерживать, чем предыдущий.
7. Использование списковых включений
Списковые включения — это краткий способ создания новых списков на основе существующих. Они позволяют вам написать одну строку кода, которая может заменить несколько строк. Синтаксис списковых включений следующий:
new_list = [expression for item in iterable if condition]
где expression— выражение, которое оценивается для каждого элемента в iterable, item— текущий элемент в iterable, и condition— необязательное условие, фильтрующее элементы, включенные в новый список. Рассмотрим следующий код:
numbers = [1, 2, 3, 4, 5] squares = [] for number in numbers: square = number ** 2 squares.append(square) print(squares)
Этот код создает список квадратов чисел в списке numbers. Мы можем переписать этот код следующим образом:
numbers = [1, 2, 3, 4, 5] squares = [number ** 2 for number in numbers]
Этот код более лаконичен и легче читается, чем предыдущий код, и он делает то же самое.
8. Использование выражений-генераторов
Выражения-генераторы похожи на генераторы списков, но вместо списков они создают генераторы. Генераторы — это объекты, которые генерируют значения на лету и более эффективно используют память, чем списки. Синтаксис выражения генератора такой же, как у генератора списка, но вместо квадратных скобок используются круглые. Например, рассмотрим следующий код:
numbers = [1, 2, 3, 4, 5] squares = (number ** 2 for number in numbers) for square in squares: print(square)
Этот код создает генератор, который генерирует квадраты чисел в списке numbers и перебирает генератор для вывода квадратов. Мы можем переписать этот код следующим образом:
numbers = [1, 2, 3, 4, 5] squares = [number ** 2 for number in numbers] for square in squares: print(square)
Этот код использует понимание списка для создания списка квадратов чисел в списке numbers, а затем выполняет итерацию по списку для вывода квадратов. Этот код работает, но он создает список в памяти, что может быть неэффективно для больших наборов данных. Мы можем переписать этот код, используя выражение-генератор, следующим образом:
numbers = [1, 2, 3, 4, 5] squares = (number ** 2 for number in numbers) for square in squares: print(square)
Этот код создает генератор, который генерирует квадраты чисел в списке numbers и перебирает генератор для в вывода квадратов. Этот код более эффективно использует память, чем предыдущий, и делает то же самое.
9. Использование функции map
Функция map— это встроенная в Python функция, которая применяет функцию к каждому элементу итерируемого объекта и возвращает новый итерируемый объект с результатами. Синтаксис функции map следующий:
map(function, iterable)
где function— функция, применяемая к каждому элементу массива iterable, и iterable— итерация, к которой применяется функция. Например, рассмотрим следующий код:
numbers = [1, 2, 3, 4, 5] def square(number): return number ** 2 squares = map(square, numbers) for square in squares: print(square)
Этот код использует функцию map для применения функции square к каждому числу в списке numbers и создания новой итерации квадратов. Этот код более лаконичен и легче читается, чем предыдущий.
10. Использование функции filter
Функция filter— это встроенная в Python функция, которая фильтрует итерируемый объект на основе функции, возвращающей логическое значение. Синтаксис функции filter следующий:
filter(function, iterable)
где function — функция , которая фильтрует iterable, а iterable является итератором для фильтрации. Например, рассмотрим следующий код:
numbers = [1, 2, 3, 4, 5] def is_even(number): return number % 2 == 0 evens = filter(is_even, numbers) for even in evens: print(even)
Этот код использует функцию filter для фильтрации списка numbers на основе функции is_even и создания новой итерации четных чисел. Этот код более лаконичен и легче читается, чем предыдущий код, но делает он то же самое.
Заключение
В этой статье мы обсудили некоторые советы и рекомендации по написанию лучшего и более эффективного кода Python. Эти советы и рекомендации включают в себя использование функции enumerate, функции zip и других встроенных функций, таких как понимание списков, выражения-генератора, map и filter. Используя эти методы, вы можете писать более краткий, модульный и читаемый код, который также является более эффективным и масштабируемым.
Однако написание эффективного кода Python — это не только использование правильных методов. Это также требует хороших методов программирования, таких как написание ясного и лаконичного кода, использование осмысленных имен переменных и функций, комментирование кода и регулярное его тестирование. Следуя этим практикам и используя методы, которые мы обсуждали в этой статье, вы можете стать более эффективным и действенным программистом на Python.
Как оптимизировать код на Python
Считается, что первоочередной задачей программиста является написание чистого и эффективного кода. Как только вы создали чистый код, можете переходить к следующим 10 подсказкам. Я подробно объясню их ниже.
Как я измеряю время и сложность кода?
Я пользуюсь Python профайлером, который измеряет пространственную и временную сложность программы. Вести журнал производительности можно через передачу дополнительного файла вывода с помощью параметра -о.
python -m cProfile [-o output_file] my_python_file.py
Используйте структуры данных из хеш-таблиц
- Если ваше приложение будет выполнять огромное количество операций поиска на большой коллекции неповторяющихся элементов, то воспользуйтесь словарем.
- Это высокопроизводительная коллекция данных.
- Сложность поиска элемента — O(1).
- Здесь стоит упомянуть, что словари не эффективны для наборов данных с малым количеством элементов.
items = [‘a’, ‘b’. ’100m’] #1000s of itemsfound = False
for i in items:
if (i == ‘100m’):
found = True
items = #each item is key/value
found = False
if ‘100m’ in items:
found = True
Если есть такая возможность, то вместо перебора данных коллекций пользуйтесь поиском.
Векторизация вместо циклов
Присмотритесь к Python-библиотекам, созданным на С (Numpy, Scipy и Pandas), и оцените преимущества векторизации. Вместо прописывания цикла, который раз за разом обрабатывает по одному элементу массива М, можно выполнять обработку элементов одновременно. Векторизация часто включает в себя оптимизированную стратегию группировки.
import numpy as np
array = np.array([[1., 2., 3.], [4., 5., 6.]])m_array = array*array
Сократите количество строк в коде
Пользуйтесь встроенными функциями Python. Например, map()
newlist = []def my_fun(a):
return a + ‘t’for w in some_list:
newlist.append(my_fun(w))
def my_fun(a):
return a + ‘t’newlist = map(my_fun, some_list)
Каждое обновление строковой переменной создает новый экземпляр
my_var = ‘Malik’
myname_blog = ‘Farhad ‘ + my_var + ‘ FinTechExplained’
my_var = ‘Malik’
myname_blog = ‘Farhad FinTechExplained’.format(my_var)
Пример выше уменьшает объем памяти.
Для сокращения строк пользуйтесь циклами и генераторами for
for x in big_x:
for y in x.Y:
items.append(x.A + y.A)
items = [x.A+y.A for x in big_x for y in x.Y]
Пользуйтесь многопроцессорной обработкой
Если ваш компьютер выполняет более одного процесса, тогда присмотритесь к многопроцессорной обработке в Python.
Она разрешает распараллеливание в коде. Многопроцессорная обработка весьма затратна, поскольку вам придется инициировать новые процессы, обращаться к общей памяти и т.д., поэтому пользуйтесь ей только для большого количества разделяемых данных. Для небольших объемов данных многопроцессорная обработка не всегда оправдана.
def some_func(d):
#computationsdata = [1,2. 10000] #large datafor d in data:
some_func(d)
import multiprocessingdef some_func(d):
#computationsdata = [1,2. 10000] #large datapool = multiprocessing.Pool(processes=number_of_processors)r = pool.map(some_func, data)
pool.close()
Многопроцессорная обработка очень важна для меня, поскольку я обрабатываю по несколько путей выполнения одновременно.
Пользуйтесь Cython
Cython — это статический компилятор, который будет оптимизировать код за вас.
Загрузите расширения Cythonmagic и пользуйтесь тегом Cython для компиляции кода через Cython.
Воспользуйтесь Pip для установки Cython:
pip install Cython
Для работы с Cython:
%load_ext cythonmagic
%%cython
def do_work():
… #работа с большим объемом вычислений
Пользуйтесь Excel только при необходимости
Не так давно мне нужно было реализовать одно приложение. И мне бы пришлось потратить много времени на загрузку и сохранение файлов из/в Excel. Вместо этого я пошел другим путем: создал несколько CSV-файлов и сгруппировал их в отдельной папке.
Примечание: все зависит от задачи. Если создание файлов в Excel сильно тормозит работу, то можно ограничиться несколькими CSV-файлами и утилитой на нативном языке, которая объединит эти CSV в один Excel-файл.
df = pd.DataFrame([[‘a’, ‘b’], [‘c’, ‘d’]],index=[‘row 1’, ‘row 2’],columns=[‘col 1’, ‘col 2’])
df.to_excel(“my.xlsx”)df2 = df.copy()
with pd.ExcelWriter(‘my.xlsx’) as writer:
df.to_excel(writer, sheet_name=’Sheet_name_1')
df2.to_excel(writer, sheet_name=’Sheet_name_2')
df = pd.DataFrame([[‘a’, ‘b’], [‘c’, ‘d’]],index=[‘row 1’, ‘row 2’],columns=[‘col 1’, ‘col 2’])
df2 = df.copy()df.to_csv(“my.csv”)
df2.to_csv(“my.csv”)
Пользуйтесь Numba
Это — JIT-компилятор (компилятор «на лету»). С помощью декоратора Numba компилирует аннотированный Python- и NumPy-код в LLVM.
Разделите функцию на две части:
1. Функция, которая выполняет вычисления. Ее декорируйте с @autojit.
2. Функция, которая выполняет операции ввода-вывода.
from numba import jit, autojit@autojit
def calculation(a):
….
def main():
calc_result = calculation(some_object)
d = np.array(calc_result)
#save to file
return d
Пользуйтесь Dask для распараллеливания операций Pandas DataFrame
Dask очень классный! Он помог мне с параллельной обработкой множества функций в DataFrame и NumPy. Я даже попытался масштабировать их в кластере, и все оказалось предельно просто!
import pandas as pd
import dask.dataframe as dd
from dask.multiprocessing import getdata = pd.DataFrame(…) #large data setdef my_time_consuming_function(d):
…. #долго выполняемая функцияddata = dd.from_pandas(data, npartitions=30)def apply_my_func(df):
return df.apply(
(lambda row: my_time_consuming_function(*row)), axis=1)def dask_apply():
return ddata.map_partitions(apply_my_func).compute(get=get)
Пользуйтесь пакетом swifter
Swifter использует Dask в фоновом режиме. Он автоматически рассчитывает наиболее эффективный способ для распараллеливания функции в пакете данных.
Это плагин для Pandas.
import swifter
import pandas as pda_large_data_frame = pd.DataFrame(…) #large data set
def my_time_consuming_function(data):
…
result = a_large_data_frame.swifter.apply(some_function)
Пользуйтесь пакетом Pandarallel
Pandarallel может распараллеливать операции на несколько процессов.
Опять же, подходит только для больших наборов данных.
from pandarallel import pandarallel
from math import sin
pandarallel.initialize()
# ALLOWED
def my_time_consuming_function(x):
….
df.parallel_apply(my_time_consuming_function, axis=1)
Общие советы
- Первым делом нужно писать чистый и эффективный код. Мы должны проследить, чтобы код внутри цикла не выполнял одни и те же вычисления.
- Также важно не открывать/закрывать подключения ввода-вывода для каждой записи в коллекции.
- Подумайте, можно ли кэшировать объекты.
- Проверьте, что не создаете новые экземпляры объектов там, где они не нужны.
- И, наконец, убедитесь, что код написан лаконично и не выполняет одни и те же повторяющиеся задачи со сложными вычислениями.
Как только вы добились чистого кода, можно приступать к рекомендациям, описанным выше.
Заключение
В данной статье были даны краткие подсказки по написанию кода. Они будут весьма полезны для тех, кто хочет улучшить производительность Python-кода.
