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

Най-често се предлагат следните решения:
- Използвайте Psyco
- Пренапишете част от програмата на C, като използвате Python C Extensions
- Променете алгоритъма на мозъка
И така, какво да правя?
Тогава, ако горните методи не работят за вашия проект, какво трябва да направите? Промяна на Python на друг език? Не, не можеш да се откажеш. Нека оптимизираме самия код.Примерите ще бъдат взети от програма, която изгражда набор на Манделброт с даден размер с даден брой итерации. Времето на работа на оригиналната версия с параметри 600*600 пиксела, 100 итерации беше3,07 сек, ще приемем тази стойност като 100%
Позволете ми да кажа предварително, че някои от оптимизациите ще направят кода по-малко питоничен, последователите на Python-way ме извиняват.
Стъпка 0. Прехвърляне на основния програмен код в отделен
Тази стъпка помага на интерпретатора на Python да прави по-добри вътрешни оптимизации при стартиране, а когато използвате psyco, тази стъпка също може да бъде голяма помощ. psyco оптимизира само функциите, без да засяга основното тяло на програмата. Ако преди изчислителната част на оригиналната програма изглеждаше така:
След това, като го промените на:
получихме времето2,4 сек, т.е.78%от оригинала.
Стъпка 1 Профилиране
Стандартната библиотека на Python е просто колекция от полезни модули. Сега се интересуваме от модула cProfile, благодарение на който профилирането на код става лесно и дори интересно. Пълната документация за този модул може да бъде намерена тук, но няколко прости команди ще са достатъчни за нас.
python -m cProfile sample.pyКлючИнтерпретаторът -m ви позволява да изпълнявате модули като отделни програми, ако самият модул предоставя такава възможност.Резултатът от тази команда ще бъде да получите "профила" на програмата - таблица във формата 4613944 извиквания на функции (4613943 примитивни извиквания) за 2,818 секунди
Подредени по: вътрешно време
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, сортирайки изхода на профайлъра по време на изпълнение.
В моя случай интересната част от изхода беше (времето за изпълнение е различно от горното, тъй като профилиращият добавя своите "режимни разходи"): 4613944 извиквания на функции (4613943 примитивни извиквания) за 2,818 секунди
Подредени по: вътрешно време
mand_slow.py:22(mandelbrot) 3533224 0.296 0.000 0.296 0.000 360000 0.081 0.00 0 0.081 0.000 360000 0.044 0.000 0.044 0.000 36 0000 0,036 0,000 0,036 0,000 . И така, профилът е получен, сега да преминем към оптимизацията.
Стъпка 2. Анализ на профила
Виждаме, че основната ни функция mandelbrot е на първо място по отношение на времето, следвана от функцията abs system, следвана от няколко функции от математическия модул, след това извиквания на единични функции, с минимални времеви разходи, ние не се интересуваме от тях.
И така, функциите на системата, "облизани" от общността, е малко вероятно да можем да подобрим, така че нека да преминем към нашия собствен код:
Стъпка 3: Математика
Сега кодът изглежда така:
Обърнете внимание, че операторът за степенуване ** е доста „общ“, но имаме нужда само от степенуване до втора степен, т.е. всички конструкции от формата x**2 могат да бъдат заменени с x*x, като по този начин се печели още време. Нека да разгледаме времето:1,9 сек, или62%от първоначалното време, постигнато чрез просто заместване на двата реда:
Стъпки 5, 6 и 7. Малки, но важни
Обща истина, която всички програмисти на Python знаят, е, че работата с глобални променливи е по-бавна от работата с локални. Но често се забравя фактът, че това е вярно не само за променливите, но като цяло за всички обекти. Функционалният код извиква няколко функции от математическия модул. Така че защо не ги импортирате в самата функция? Произведено:
Още 0,1 секунди възстановени. Припомнете си, че abs(x) ще върне число с плаваща единица. Така че си струва да го сравните с float, а не с int:
Още 0,15 сек.53%от началното време.
И накрая, един мръсен хак. В тази конкретна задача можете да разберете, че долната половина на изображението е равна на горната, така че. броят на изчисленията може да бъде намален наполовина, което води до0,84 секили27%от първоначалното време.
Заключение
Профил. Използвайте timeit. Оптимизиране. Python е мощен език и програмите в него ще работят със скорост, пропорционална на вашето желание да разберете и излъскате всичко :) Целта на тази статия е да покаже, че чрез малки и незначителни промени, като замяна на ** с *, можете да накарате зелена змия да пълзидо два пъти по-бързо, без да използвате тежка артилерия под формата на C или психически шаманизъм. Също така можете да комбинирате различни инструменти, като горните оптимизации и психическия модул, няма да стане по-лошо :)
UPDFunca предостави полезна връзка в коментарите.
Hardcore conf в C++. Каним само професионалисти.