Рубріки: Решения

В 20 тысяч раз быстрее: 5 приемов для ускорения кода на Python

Богдан Мирченко

Несмотря на то, что Python — один из самых популярных языков программирования в мире, он не лишен недостатков. Самый большой из них, о котором, вероятно, известно всем — это скорость. О пяти способах улучшить Python-код в блоге на Dice рассказал разработчик программного обеспечения Дэвид Болтон. 

Примечание: автор протестировал способы на Python 3.7 и 3.9, а чтобы вести отсчет времени в наносекундах, использовал функцию perf_counter_ns из пакета time. 

Pythonic

Простыми словами, Pythonic — стиль кода. Поэтому, говоря, что какой-либо код — pythonic — имеется в виду, что он написан в соответствии с идиомами Python. Это использование таких функций как map, sum и range, а также понимание списков и генераторов. 

Например, нужно сравнить два способа подсчета всех целых чисел в диапазоне от 1-100, которые кратны 3:

from time import perf_counter_ns

def main():    
    # non pythonic
    start=perf_counter_ns()
    total=0 
    for i in range(1,100):
        if (i %3)== 0:
            total += i
    end=perf_counter_ns()      
    print (f"Non-Pythonic Total of divisible by 3= {total}")
    print(f"Time took {end-start}")

    # pythonic
    start=perf_counter_ns()
    total =sum(range(1, 100, 3))
    end=perf_counter_ns()      
    print (f"Pythonic Total of divisible by 3= {total}")
    print(f"Time took {end-start}")

if __name__ == "__main__":
    main()

Это дает нам:

Non-Pythonic Total of divisible by 3= 1683
Time took 13300
Pythonic Total of divisible by 3= 1683
Time took 2900

Это время второго прогона. Первые запуски были 14,500 и 3,000, то есть от 3,5% до 9% дольше. В данном случае Pythonic-код почти в пять раз быстрее обычного. 

Мемоизация

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

В пакете functools есть lru_cache, который можно использовать для оформления функции, которую нужно мемоизировать. В приведенном ниже примере: 

  • fib — это простая немемоизированная функция Фибоначчи;
  • fib(35) делает много сложений;
  • mfib — это мемоизированная версия.
from time import perf_counter_ns
from functools import lru_cache

def main():    
    def fib(n):
        return n if n < 2 else fib(n-1) + fib(n-2)

    @lru_cache(maxsize=None)
    def mfib(n):
        return n if n < 2 else mfib(n-1) + mfib(n-2)
    start=perf_counter_ns()
    print(f"Non-memoized fib()={fib(35)}")
    end=perf_counter_ns()      
    print(f"Time took {end-start}")

    start=perf_counter_ns()  
    print(f"Memoized fib()={mfib(35)}")
    end=perf_counter_ns()      
    print(f"Time took {end-start}")

if __name__ == "__main__":
    main()

Результат говорит сам за себя:

Non-memoized fib()=9227465
Time took 2905175700
Memoized fib()=9227465
Time took 148700

Код выполнился почти в 20 тысяч раз быстрее. 

Кстати, разработчик Орен Тош считает, что код можно еще ускорить, используя подкласс Dictionary с методом __missing__ dunder. 

Перевод кода на С

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

В библиотеке ctypes есть библиотека типов C и их отображений в Python. Она также позволяет обращаться к библиотекам операционной системы, но нужно быть готовым работать на достаточно низком уровне и знать язык С, включая массивы, структуры и указатели. 

Компиляция Python

Машинный код, который создается при компиляции кода, всегда будет работать быстрее, чем интерпретируемый байт-код. Есть несколько компиляторов Python, включая Numpa, Nuitka, pypi и Cython. Автор советует оптимизировать код Python, прежде чем пытаться компилировать его. Компилятор Numpa — JIT (Just-In-Time), который также обеспечивает ускорение на GPU. 

Использование from

Можно постоянно использовать пакет import, но более разумно использовать from, когда можно импортировать только нужную функцию (или функции). Зачем импортировать 20 функций, если нужна только одна? Для таких коротких программ, как ниже, вероятно, разница не будет заметна, но с увеличением размера программы она станет очевидна:

from time import perf_counter_ns

def main():
    start=perf_counter_ns()
    n = 10
    fact = 1
  
    for i in range(1,n+1):
        fact = fact * i
    end=perf_counter_ns()      
    print (f"The factorial of {n} is {fact}")
    print(f"Time took {end-start}")

if __name__ == "__main__":
    main()

При первом запуске было получено 4200 наносекунд, а при последующих — около 3900. Не забывайте, что можно помещать импорты внутрь функций, чтобы они вызывались только тогда, когда это необходимо. 

Заключение

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

Останні статті

Что такое прокси-сервер: пояснение простыми словами, зачем нужны прокси

Прокси (proxy), или прокси-сервер — это программа-посредник, которая обеспечивает соединение между пользователем и интернет-ресурсом. Принцип…

21.11.2024

Что такое PWA приложение? Зачем необходимо прогрессивное веб-приложение

Согласитесь, было бы неплохо соединить в одно сайт и приложение для смартфона. Если вы еще…

19.11.2024

Как создать игру на телефоне: программирование с помощью конструктора

Повсеместное распространение смартфонов привело к огромному спросу на мобильные игры и приложения. Миллиарды пользователей гаджетов…

17.11.2024

Google Bard: эффективный аналог ChatGPT

В перечне популярных чат-ботов с искусственным интеллектом Google Bard (Gemini) еще не пользуется такой популярностью…

14.11.2024

Скрипт и программирование: что это такое простыми словами

Скрипт (англ. — сценарий), — это небольшая программа, как правило, для веб-интерфейса, выполняющая определенную задачу.…

12.11.2024

Дедлайн в разработке: что это такое простыми словами

Дедлайн (от англ. deadline — «крайний срок») — это конечная дата стачи проекта или задачи…

11.11.2024