Olej писал(а):
Вообще то, Cython - не совсем альтернатива, это своеобразный "клон" языка Python вообще, с совместимым синтаксисом, но позволяющий, с наложением некоторых ограничений на статические определения типов, компилировать ту же программу (в отличие от интерпретации байт-кода Python), что позволяет ускорить выполнение, по некоторым данным, до 100-1000 раз!
Мне кажется, что вот это такой интересный пример:
- 3 эквивалентные по функциональности реализации - длинный цикл тупых математических вычислений, в данном случае с функцией sin(x), но это не важно...
- а). чисто на Python вариант и выполняемый в классическом
интерпретаторе CPython, б). то же, но
компилировано Cython, в). та же компиляция Cython, но вместо вызова math.sin( x ) из Python модуля math, вызывается C-реализация sin( x ) из стандартной C-библиотеки libm.so.
- ... и контролируем время выполнения...
- файл psin.py, реализующий вариант а). :
Код: Выделить всё
import time
import math
def sin0( n ):
x = 0.0
clc = time.time()
for i in xrange( n ):
x += math.sin( x )
return time.time() - clc
- файл csin.pyx, реализующий варианты б). и в). :
Код: Выделить всё
import time
import math
def sin1( int n ):
cdef int i
cdef double x, clc
x = 0.0
clc = time.time()
for i in xrange( n ):
x += math.sin( x )
return time.time() - clc
cdef extern from "math.h":
double sin( double )
def sin2( n ):
x = 0.0
clc = time.time()
for i in xrange( n ):
x += sin( x )
return time.time() - clc
- файл setup.py для сборки модуля csin.so (обратите внимание как подключена стандартная библиотека libm.so) :
Код: Выделить всё
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
ext_modules = [ Extension( "csin", [ "csin.pyx" ], libraries=["m"] ) ]
setup(
cmdclass = { 'build_ext': build_ext },
ext_modules = ext_modules
)
- файл сборки Makefile (можно и вручную собрать той же командой):
Код: Выделить всё
all *.so: setup.py
python setup.py build_ext --inplace
clean:
rm -f *.c *.so *.pyc *.pyo
rm -f -R build
test: *.so
python test.py
- и тестовый файл test.py, которым это всё проверяется:
Код: Выделить всё
#!/usr/bin/python
# -*- coding: utf-8 -*-
from sys import argv
from psin import sin0 as t1
from csin import sin1 as t2
from csin import sin2 as t3
#from csin import sin3 as t4
n = ( len( argv ) > 1 and int( argv[ 1 ] ) ) or 5000000
print "время %i вычислений %5.2f секунд" % ( n, t1( n ) )
print "время %i вычислений %5.2f секунд" % ( n, t2( n ) )
print "время %i вычислений %5.2f секунд" % ( n, t3( n ) )
А теперь выполнение ... достаточно неожиданное:
Код: Выделить всё
bash-4.2$ ./test.py 10000000
время 10000000 вычислений 4.81 секунд
время 10000000 вычислений 5.33 секунд
время 10000000 вычислений 0.29 секунд
- время компилированного выполнения цикла даже
больше простой интерпретации в CPython, хотя переменные цикла и типизированы -
http://kostikvento.ru/cython/docs/src/quickstart/cythonize.html:
Поскольку для переменной цикла i явно указан тип из семантики C, цикл for будет непосредственно преобразован в чистый код на C. Объявление типов для переменных a, s и dx существенно, поскольку они участвуют в вычислениях внутри цикла; типирование b и N не дает особого вклада, зато мы будем последовательны и применим типирование переменных во всем коде функции (в данном случае для этого не придется много работать).
Это приводит уже к 4-кратному ускорению по сравнению с версией на чистом Питоне.
- это связано, полагаю, с существенным временем самого вычисления sin( x ) в штатном модуле math,
намного больше самого времени операций в цикле...
- но вот подключенный для тех же целей код C выполняется
в 20 раз быстрее.
P.S. не ускорение операций в компилируемом коде нужно списать, думаю, ещё и на постоянно улучшающуюся от версии к версии производительность интерпретирующей системы байт-кода CPython.