Ускорение Питона
Это на самом деле два вопроса, но они настолько похожи, и, чтобы было проще, я решил, что я просто свожу их вместе:
Во-первых: Учитывая существующий проект Python, каковы некоторые достойные способы ускорить его, кроме простой оптимизации в коде?
Во-вторых: при написании программы с нуля на python, какие есть хорошие способы значительно повысить производительность?
Что касается первого вопроса, представьте, что вы получили достойно написанный проект, и вам нужно улучшить производительность, но вы не можете получить большую выгоду от рефакторинга / оптимизации. Что бы вы сделали, чтобы ускорить его в этом случае, за исключением переписывания чего-то вроде C?
19 ответов
Относительно "Во-вторых: при написании программы с нуля на python, какие есть хорошие способы значительно повысить производительность?"
Запомните правила оптимизации Джексона:
- Правило 1: не делай этого.
- Правило 2 (только для экспертов): пока не делайте этого.
И правило Кнута:
- "Преждевременная оптимизация - корень всего зла".
Более полезные правила содержатся в Общих правилах оптимизации.
Не оптимизируйте, как вы идете. Сначала поймите правильно. Тогда сделай это быстро. Оптимизация неправильной программы по-прежнему неверна.
Помните правило 80/20.
Всегда запускайте тесты "до" и "после". В противном случае вы не узнаете, нашли ли вы 80%.
Используйте правильные алгоритмы и структуры данных. Это правило должно быть первым. Ничто так не важно, как алгоритм и структура данных.
Нижняя линия
Вы не можете предотвратить или избежать усилий по "оптимизации этой программы". Это часть работы. Вы должны планировать это и делать это тщательно, точно так же как дизайн, код и действия по тестированию.
Вместо того, чтобы просто доехать до C, я бы предложил:
Сделайте ваш счетчик кода. Делайте больше с меньшим количеством исполнений строк:
- Измените алгоритм на более быстрый. Во многих случаях это не должно быть модным, чтобы быть быстрее.
- Используйте python-примитивы, написанные на C. Некоторые вещи заставят переводчика отправлять туда, где это не нужно. Последнее предпочтительнее
- Остерегайтесь кода, который сначала создает большую структуру данных, а затем использует ее. Подумайте о разнице между диапазоном и xrange. В общем, часто стоит задуматься об использовании памяти программой. Использование генераторов может иногда снизить использование памяти O(n) до O (1).
- Python, как правило, не оптимизирует. Поднимайте инвариантный код из циклов, по возможности устраняйте общие подвыражения в тесных циклах.
- Если что-то дорогое, то предварительно вычислите или запомните это. Регулярные выражения могут быть скомпилированы, например.
- Нужно хрустеть числами? Вы можете проверить
numpy
из. - Многие программы на python работают медленно, потому что они связаны с дисковым вводом-выводом или доступом к базе данных. Убедитесь, что у вас есть что-то стоящее, пока вы ожидаете получения данных, а не просто блокируете их. Оружие может быть что-то вроде
Twisted
фреймворк. - Обратите внимание, что многие важные библиотеки обработки данных имеют C-версии, будь то XML, JSON или еще много чего. Они часто значительно быстрее, чем интерпретатор Python.
Если все вышеперечисленное не работает для профилированного и измеренного кода, тогда начинайте думать о пути C-rewrite.
Обычные подозреваемые - профилируйте это, найдите самую дорогую линию, выясните, что она делает, исправьте это. Если вы раньше не выполняли много профилирования, могут быть большие жирные квадратичные циклы или дублирование строк, скрывающиеся за другими безобидно выглядящими выражениями.
В Python две наиболее распространенные причины неявного замедления, которые я обнаружил, - это конкатенация строк и генераторы. Так как строки Python неизменны, делаем что-то вроде этого:
result = u""
for item in my_list:
result += unicode (item)
скопирует всю строку дважды за итерацию. Это было хорошо освещено, и решение заключается в использовании "".join
:
result = "".join (unicode (item) for item in my_list)
Генераторы являются еще одним виновником. Они очень просты в использовании и могут значительно упростить некоторые задачи, но плохо примененный генератор будет намного медленнее, чем просто добавление элементов в список и возврат списка.
Наконец, не бойтесь переписывать биты в C! Python как динамический язык высокого уровня просто не способен соответствовать скорости Си. Если есть одна функция, которую вы больше не можете оптимизировать в Python, рассмотрите возможность ее извлечения в модуль расширения.
Моя любимая техника для этого - поддерживать версии модуля на Python и C. Версия Python написана так, чтобы быть максимально понятной и очевидной - любые ошибки должны легко диагностироваться и исправляться. Напишите свои тесты против этого модуля. Затем напишите версию C и протестируйте ее. Его поведение во всех случаях должно быть таким же, как и в реализации Python - если они различаются, должно быть очень легко выяснить, что является неправильным, и исправить проблему.
Первое, что приходит на ум: психо. Пока работает только на x86.
Затем постоянная привязка. То есть, сделать все глобальные ссылки (и global.attr, global.attr.attr…) локальными именами внутри функций и методов. Это не всегда успешно, но в целом это работает. Это можно сделать вручную, но, очевидно, это утомительно.
Вы сказали, что кроме оптимизации в коде, поэтому я не буду углубляться в это, но держу ваш разум открытым для типичных ошибок (for i in range(10000000)
приходит в голову), что люди делают.
Cython и pyrex могут использоваться для генерации кода c с использованием Python-подобного синтаксиса. Psyco также отлично подходит для подходящих проектов (иногда вы не заметите большого увеличения скорости, иногда это будет в 50 раз быстрее). Я все еще считаю, что лучший способ - профилировать ваш код (cProfile и т. Д.), А затем просто кодировать узкие места как функции c для python.
Я удивлен, что никто не упомянул ShedSkin: http://code.google.com/p/shedskin/, он автоматически конвертирует вашу программу на Python в C++ и в некоторых тестах дает лучшие улучшения в скорости, чем psyco.
Плюс анекдотические истории о простоте: http://pyinsci.blogspot.com/2006/12/trying-out-latest-release-of-shedskin.html
Однако есть ограничения, см. http://tinyurl.com/shedskin-limitations
Я надеюсь, что вы прочитали: http://wiki.python.org/moin/PythonSpeed/PerformanceTips
Для возобновления уже есть 3 принципа:
- написать код, который преобразуется в лучший байт-код, например, использовать локальные, избегать ненужных поисков / вызовов, использовать идиоматические конструкции (если есть естественный синтаксис для того, что вы хотите, используйте его - обычно быстрее. например: не делать: "для ввода ключа some_dict.keys()", do " для ввода ключа some_dict")
- все, что написано на C, значительно быстрее, злоупотребляйте любыми доступными вам функциями / модулями C
- если есть сомнения, время импорта, профиль
Люди дали несколько полезных советов, но вы должны знать, что когда требуется высокая производительность, модель python выглядит так: pnt to c. Такие усилия, как psyco, могут в будущем немного помочь, но python просто не быстрый язык, и он не предназначен для этого. Очень немногие языки способны действительно хорошо выполнять динамические вещи и все же генерировать очень быстрый код; по крайней мере, в обозримом будущем (и некоторые проекты работают против быстрой компиляции), что будет иметь место.
Так что, если вы действительно окажетесь в этой привязке, лучше всего будет изолировать части вашей системы, которые недопустимо медленны в (хорошем) питоне, и разработать идею, что вы перепишете эти биты в C. Извините. Хороший дизайн может помочь сделать это менее болезненным. Прототипируйте его сначала в python, а затем вы легко можете проверить работоспособность вашего c.
Это работает достаточно хорошо для таких вещей, как NumPy, в конце концов. Хотя я не могу особо подчеркнуть, насколько хороший дизайн поможет вам. Если вы просто итеративно ткнете в свои биты Python и заменяете самые медленные на C, вы можете столкнуться с большим беспорядком. Подумайте, где именно нужны биты C, и как их можно минимизировать и разумно инкапсулировать.
Запустите ваше приложение через профилировщик Python. Найти серьезное узкое место. Перепишите это узкое место на C. Повторите.
Это не обязательно ускорит ваш код, но является критически важным при программировании на Python, если вы хотите избежать замедления кода. "Глобальная блокировка интерпретатора" (GIL) может значительно снизить скорость вашей многопоточной программы, если ее поведение не понято (да, это немного меня… У меня была хорошая 4-процессорная машина, которая не будет использовать более 1,2 процессоров одновременно). На SmoothSpan есть вводная статья с несколькими ссылками для начала работы.
Часто можно достичь скоростей, близких к C (достаточно близко для любого проекта, использующего Python, в первую очередь!), Заменив явные алгоритмы, написанные от руки в Python, неявным алгоритмом, использующим встроенный вызов Python. Это работает, потому что большинство встроенных Python написаны на C в любом случае. Ну, конечно, в CPython;-) https://www.python.org/doc/essays/list2str/
Просто примечание об использовании psyco: в некоторых случаях это может привести к медленному времени выполнения. Особенно, когда я пытаюсь использовать psyco с кодом, написанным на C. Я не могу вспомнить статью, которую я прочитал, но map()
а также reduce()
функции были упомянуты специально. К счастью, вы можете сказать psyco не обрабатывать указанные функции и / или модули.
Это процедура, которой я стараюсь следовать:
- импорт психо; psyco.full()
- Если это не достаточно быстро, запустите код через профилировщик, посмотрите, где узкие места. (ОТКЛЮЧИТЕ психо для этого шага!)
- Старайтесь делать то, что упоминали другие, чтобы получить код в этих узких местах как можно быстрее.
- Такие вещи, как [str(x) для x в l] или [x.strip() для x в l], намного, намного медленнее, чем map(str, x) или map(str.strip, x).
- После этого, если мне все еще нужно больше скорости, на самом деле очень легко запустить PyRex. Сначала я скопирую часть кода Python, поместил его прямо в код Pyrex и посмотрю, что произойдет. Тогда я вертлюсь с этим, пока это не становится все быстрее и быстрее.
Каноническая ссылка на то, как улучшить код Python, находится здесь: PerformanceTips. Я бы рекомендовал не оптимизировать в C, если вам действительно не нужно. Для большинства приложений вы можете получить необходимую производительность, следуя правилам, размещенным в этой ссылке.
Если вы используете psyco, я бы порекомендовал psyco.profile()
вместо psyco.full()
, Для более крупного проекта будет разумнее использовать функции, которые были оптимизированы, и использовать меньше памяти.
Я также рекомендовал бы взглянуть на итераторы и генераторы. Если ваше приложение использует большие наборы данных, это сэкономит вам много копий контейнеров.
Помимо (отличного) психо и (симпатичного) шедска, я бы порекомендовал попробовать Cython как отличную вилку из пирекс.
Или, если вы не спешите, я рекомендую просто подождать. Новые виртуальные машины Python выходят, и unladen-swallow найдет свой путь в мейнстрим.
Я считаю, что для существующего проекта основной выигрыш в производительности будет заключаться в максимально возможном использовании внутренней библиотеки Python.
Некоторые советы здесь: http://blog.hackerearth.com/faster-python-code
Также существует транспилятор Python → 11l → C++, который можно скачать отсюда.
После того, как был задан этот вопрос, было представлено несколько способов ускорить код на Python:
- Pypy имеет JIT-компилятор, который делает его намного быстрее для кода, связанного с процессором.
- Pypy написан на Rpython, подмножестве Python, которое компилируется в нативный код, используя цепочку инструментов LLVM.