Одно из распространенных применений Python — небольшие скрипты для обработки данных (например, некоторых логов).
Мне часто приходилось сталкиваться с такими задачами, сценарии обычно писались на скорую руку.
Вкупе с моим плохим знанием алгоритмов это привело к тому, что код оказался далёк от оптимального.
Меня это ничуть не расстроило: лишняя минута исполнения ничего не изменит. Ситуация несколько изменилась по мере увеличения объема обрабатываемых данных.
И после того, как время выполнения очередного скрипта превысило сутки, я решил уделить немного времени оптимизации — ведь хотелось бы получить результат до того, как он потеряет свою актуальность.
В этой статье я не планирую говорить о профилировании, а затрону тему компиляции Python-кода.
При этом опишу условие: варианты оптимизации не должны требовать времени разработчика, а наоборот, быть дружелюбными к «пыхту-пыху и продакшену».
Для бенчмарка я сделал два скрипта, решающих абсолютно синтетические задачи:
- generate.py — скрипт генерирует 500 тысяч словарей с одинаковыми ключами и разными значениями, сериализует их в json и записывает в файл.
Получается что-то вроде:
{"str_youCPQmO": "TMrjhoKpFuyZ", "int_VAuUXXmC": 5, "int_ScRduCVX": 73, "str_YfsEUEve": "IOAYDoAuZBzQ", "int_dlBzZYlO": 77, "int_lJaDHVSH": 45, "str_qzDCDxbm": "rfERFTVFZiku", "int_gblmMsBX": 57, "str_MZNfINjj": "DeDaDMjKQyzo", "str_sUQVbIyn": "tenhduEcWkof"} {"str_youCPQmO": "OJRZDmiQxflr", "int_VAuUXXmC": 9, "int_ScRduCVX": 32, "str_YfsEUEve": "CYxuIUTWAVTH", "int_dlBzZYlO": 37, "int_lJaDHVSH": 22, "str_qzDCDxbm": "aZTizzobHBbh", "int_gblmMsBX": 63, "str_MZNfINjj": "sJElOjzNlpJZ", "str_sUQVbIyn": "WDUdOMwERjxm"}
- анализ.
py — скрипт читает сгенерированный выше файл и агрегирует их двумя способами:
- если значения представляют собой строки, то нужно найти по этому ключу наиболее используемый символ;
- если значения — числа, то от каждого из них нужно вычислить среднее значение сигмовидной функции (довольно странно, но почему бы и нет?).
Хотя очевидно, что в обычную работу вмешиваются дополнительные факторы: скрипт нужно распараллелить, можно использовать специализированные библиотеки (гораздо проще и быстрее агрегировать числа с помощью numpy/pandas) и т.д. Поскольку с самого начала я ставил перед собой требование, чтобы метод ускорения был простым, остались только те варианты, которые можно реализовать, просто листая мануалы по диагонали.
Поэтому я быстро погуглил что-то вроде «jit python», «компилятор python» и выбрал для теста:
- python 2.7 без сторонних библиотек (стандартно);
- Python 3 без сторонних библиотек;
- пипы;
- nuitka --recurse-none (компилируются только основные файлы);
- nuitka —recurse-all (компилируются все зависимости);
- онмба;
- Cython без модификации кода для статической типизации;
Однако все их можно считать решающими мою прикладную задачу — с небольшими затратами обеспечить обработку скриптов быстрее, чем их результат потеряет актуальность.
К сожалению, мне не удалось запустить numba - скрипты вылетали с исключениями типа NotImplementedError: переменные ячеек не поддерживаются.
Похоже, что нумба пока не подходит как универсальное средство ускорения всего.
Тест проводился на Macbook Pro конца 2013 года (Intel Core i5 2,4 ГГц).
Для его запуска был написан небольшой фабрикальный скрипт, поэтому любой желающий сможет легко повторить его в интересующих его условиях.
Итак, результат:
Как видите, прирост производительности варьируется от скрипта к скрипту (что неудивительно).
Тем не менее, можно отметить безоговорочную победу pypy в обеих категориях, некоторое ускорение со стороны Cython и бессмысленность использования nuitka для этих целей (что не отменяет возможности использования, если, например, нужно просто собрать воедино все зависимости ).
Интересно также, что по агрегации Python 3 оказался быстрее, чем cythonized версия того же скрипта.
Я для себя решил, что в разных случаях разумно использовать и pypy, и Cython: например, если скрипт вовсю использует numpy/scipy/pandas и т.д., то с pypy могут возникнуть трудности (не весь этот стек работает из коробки с pypy), но перевести одну тяжелую функцию на Cython будет довольно легко.
Исходники тестов есть на Гитхабе , улучшения и дополнения приветствуются.
Теги: #python #Cython #pypy #nuitka #я ленивый #python
-
Не Заменяйте Цели Средствами
19 Oct, 24 -
Esp8266 И Arduino, Подключение, Распиновка
19 Oct, 24 -
Программирование С Помощью Pyusb 1.0
19 Oct, 24