среда, 4 февраля 2009 г.

Python и оптимизация скорости его работы

Как известно многие ругают python за низкую скорость работы, особенно это касается массированной обработки данных, различных математических операций, где отставание от С и других шустрых равняется десяткам раз.

Но существует несколько способов увеличить производительность, одними из которых является использование Psyсo или Pyrex. Для их сравнения и написан этот пост.

Во всех тестах мы будем искать все простые числа, которые меньше 50000.

Первый тест — никакой оптимизации, чистый python:
def primes(kmax):
result = []
k = 0
n = 2
while k < kmax:
i = 0
while i < k and n % result[i] != 0:
i = i + 1
if i == k:
k = k + 1
result.append(n)
n = n + 1
return result
primes(50000)
Имеем ~271 секунду:

real 4m31.119s
user 4m27.525s
sys 0m0.600s

Во втором тесте используем модуль Psyco, суть которого заключается в замене во время исполнения части интерпретируемого байткода оптимизированным машинным кодом. Работает он только на x86 Существует два варианта его применения — на весь код или на определённую функцию. Для нашего теста применялся первый вариант, нужно просто добавить в начале файла эти две строки:

import psyco
psyco.full()

Результат:

real 0m13.790s
user 0m13.733s
sys 0m0.016s

Т.е. скорость работы увеличена примерно в 20 раз относительно первого варианта.

В третем и последнем тесте для увеличение производительности используем Pyrex — специальный язык, похожий на python, предназначеный для написания модулей для python. Разработчики обещают, что скорость модулей написанных на их детище приближается к аналогам на С. Наша функция на pyrex имеет такой вид, взята на официальном сайте pyrex:
def primes(int kmax):
cdef int n, k, i
cdef int p[50000]
result = []
if kmax > 50000:
kmax = 50000
k = 0
n = 2
while k < kmax:
i = 0
while i < k and n % p[i] <> 0:
i = i + 1
if i == k:
p[k] = n
k = k + 1
result.append(n)
n = n + 1
return result
Для сборки модуля используем специальный скрипт:

from distutils.core import setup
from distutils.extension import Extension
from Pyrex.Distutils import build_ext
setup(
name = "prime",
ext_modules=[
Extension("prime", ["prime.pyx"], libraries = [])
],
cmdclass = {'build_ext': build_ext}
)
И собираем такой командой:

python setup.py build_ext —inplace

Результат:

real 0m4.346s
user 0m4.328s
sys 0m0.008s

Pyrex оказался примерно в 67 раз быстрее первого и в 3 раза быстрее второго примера.

Такие вот интересные результаты.

Psyco
Pyrex