There are many cases where you simply want to get speed up an existing Python design, and in particular code in Python to get things working, then optimize (yes, early optimization is the root of all evil, but it's even more sinister if you run out of ways to optimize your code.)
What is is good for?
The new book by Kurt Smith is well written, clear in explanations, and the best overall treatment of Cython out there. An excellent book !! The book by Gorelick and Ozsvald is a good treatment, and it compares different methods of optimizing python including Shedskin, Theano, Numba, etc.
1] Kurt W. Smith Cython, A Guide for Python Programmers, O'Reilly, January 2015
2] Mich Gorelick & Ian Ozsvald High Performance Python -- Practical Performant Programming for Humans O'Reilly September 2014
3] David Beazley and Brian K Jones, Python Cookbook, 3rd Edition, Printed May 2013, O'Reilly -- Chapter 15, page 632
It's more versatile than all the competition and has a manageable syntax. I hihgly recommend Kurt Smith's book on Cython. It's thorough, and if you read chapter 3, you will take in the essence of working with Cython functions. ***
Make sure to check out the new, improved documentation for Cython at:
http://docs.cython.org/index.html
This presentation will focus on using Cython to speed up Python functions, with some attention also given to arrays and numpy. There are more sophisticated treatments of using dynamically allocated memory, such as typically done with C and C++.
A good link on memory allocation, where the heap is used with malloc():
http://docs.cython.org/src/tutorial/memory_allocation.html?highlight=numpy
You must use "cdef" when defining a type inside of a function. For example,
def quad(int k):
cdef int alpha = 1.5
return alpha*(k**2)
People often get confused when using def, cdef, and cpdef.
The key factors are
Now, if you were going to put pure cython code into action within your editor, say Wing IDE or PyCharm, you would want to define something like this in a file say for example cy_math.pyx
Now, let's start with the familiar Fibonacci series ...
import cython
def cy_fib(int n):
"""Print the Fibonacci series up to n."""
cdef int a = 0
cdef int b = 1
cdef int index = 0
while b < n:
print ("%d, %d, \n" % (index, b) )
a, b = b, a + b
index += 1
from distutils.core import setup, Extension
from Cython.Build import cythonize
#=========================================
# Setup the extensions
#=========================================
sources = [ "cyMath.pyx", "helloCython.pyx","cy_math.pyx", "bits.pyx", "printString.pyx"]
for fileName in sources:
setup(ext_modules=cythonize(str(fileName)))
or...
map(lambda fileName : setup(ext_modules=cythonize(str(fileName))), sources)
In [41]:
%%file ./src/helloCython.pyx
import cython
import sys
def message():
print(" Hello World ....\n")
print(" Hello Central Ohio Python User Group ...\n")
print(" The 614 > 650::True")
print(" Another line ")
print(" The Python version is %s" % sys.version)
print(" The Cython version is %s" % cython.__version__)
print(" I hope that you learn something useful . . . .")
def main():
message()
In [42]:
%%file ./src/cyMath.pyx
import cython
def cy_fib(int n):
"""Print the Fibonacci series up to n."""
cdef int a = 0
cdef int b = 1
cdef int c = 0
cdef int index = 0
while b < n:
print ("%d, %d, \n" % (index, b) )
a, b = b, a + b
index += 1
In [43]:
%%file ./src/printString.pyx
import cython
def display(char *bytestring):
""" Print out a bytestring byte by byte. """
cdef char byte
for byte in bytestring:
print(byte)
In [44]:
%%file ./src/bits.pyx
import cython
def cy_reflect(int reg, int bits):
""" Reverse all the bits in a register.
reg = input register
r = output register
"""
cdef int x
cdef int y
cdef int r
x = 1 << (bits-1)
y = 1
r = 0
while x:
if reg & x:
r |= y
x = x >> 1
y = y << 1
return r
def reflect(self,s, bits=8):
""" Take a binary number (byte) and reflect the bits. """
x = 1<<(bits-1)
y = 1
r = 0
while x:
if s & x:
r |= y
x = x >> 1
y = y << 1
return r
In [45]:
%%file ./src/setup.py
from distutils.core import setup, Extension
from Cython.Build import cythonize
#=========================================
# Setup the extensions
#=========================================
sources = [ "./src/cyMath.pyx", "./src/helloCython.pyx",
"./src/cy_math.pyx", "./src/bits.pyx",
"./src/printString.pyx"]
#for fileName in sources:
# setup(ext_modules=cythonize(str(fileName)))
map(lambda fileName : setup(ext_modules=cythonize(str(fileName))), sources)
In [46]:
!python ./src/setup.py build_ext --inplace
In [47]:
from src import helloCython
helloCython.message()
In [48]:
from src import cyMath
cyMath.cy_fib(100)
In [54]:
from src import bits
from bits import cy_reflect
hexlist = [int(0x01),int(0x02),int(0x04),int(0x08)]
[hex(cy_reflect(item,8)) for item in hexlist]
In [55]:
from src import printString
printString.display('123')
In [51]:
# A little list comprehension here ...
# A comparative method to the Cython printString function
numberList = [1,2,3]
[ord(str(value)) for value in numberList]
Out[51]:
In [36]:
%%file ./src/cyFib.pyx
def cyfib(int n):
cdef int a = 0
cdef int b = 1
cdef int index = 0
while b < n:
a, b = b, a+b
index += 1
return b
https://github.com/Russell91/runcython
There is a runcython and makecython function calls . . . . .
In [ ]:
!makecython ./src/cyFib.pyx
In [ ]:
def pyfib(n):
a = 0
b = 1
index = 0
while b < n:
a, b = b, a+b
index += 1
return b
In [ ]:
%timeit pyfib(1000)
In [ ]:
import cyFib
%timeit cyFib.cyfib(1000)
In [35]:
import dis
dis.dis(pyfib)
In [56]:
import cProfile
cProfile.run('pyfib(1000)')
For now, lets begin with a polynomial function, and compare how to do this in python and cython! ....
Now consider a function such as
$f(x) = a_0x^n + a_1x^{(n-1)} + a_2x^{(n-2)} ..... a_nx^0$
where in the case below n is selected as 2, and
The cython function to do this called "cypoly" while the python version is called "pypoly". Each function is defined with a functional programming techinque of lambda and map, as shown below.
In [112]:
%%file ./src/cyPoly.pyx
def cypoly(int n, int k):
return map(lambda x:(1.0*x**2 + 0.5*x + 0.25*x), range(k))
In [113]:
!makecython ./src/cyPoly.pyx
In [114]:
def pypoly(n,k):
return map(lambda x:.1*x**2 + .5*x + 0.25*x, range(k))
Now to compare the two ....
In [116]:
from src import cyPoly
cyPoly.cypoly(4,50)
pypoly(4,50)
Out[116]:
In [62]:
%%file ./src/sineWave.pyx
import cython
from libc.math cimport sin
def sinewave(double x):
""" Calculate a sinewave for specified number of cycles, Ncycles, at a given frequency."""
return sin(x)
In [63]:
!makecython ./src/sineWave.pyx
In [64]:
from src import sineWave
import math
angle90 = math.pi/2
sineWave.sinewave(angle90)
Out[64]:
In [67]:
%matplotlib inline
import numpy as np
x = np.linspace(0,2*np.pi,2000)
%timeit plot(x,np.sin(x),'r')
## %timeit plot(x,sineWave.sinewave(x),'r') <== Why is this a problem ??
xlim(0,6.28)
title('Sinewave for Array Data')
grid(True)
In [68]:
%%file ./src/myFunc.pyx
import cython
import numpy as np
cimport numpy as np
@cython.boundscheck(False)
@cython.wraparound(False)
def myfunc(np.ndarray[double, ndim=1] A):
return np.sin(A)
In [69]:
!makecython ./src/myFunc.pyx
In [70]:
%matplotlib inline
from src import myFunc
import cython
import numpy as np
x = np.linspace(0,2*np.pi,2000)
y = myFunc.myfunc(x)
%timeit plot(x,y,'r')
xlim(0,6.28)
title('Sinewave for Array Data with Cython')
grid(True)
This talk has presented the basics of getting started with Cython and IPython/Jupyter Notebook. There were examples presented on how to compile Cython programs with a setup.py and distuils as well as a nice application, runcython. Basic programs and some programs with arrays were demonstrated.
Cython is flexible, and it's flexibility is matched by it's performance.
It's realitively easy to use, but it does have some details to watch out for when working with arrays, references, etc.
Overall
Transform your Python with Cython !!
In [71]:
!python-config --cflags
In [72]:
!python-config --ldflags
In [91]:
!ls -a ./src
In [169]:
%%file ./src/quad.pyx
"""
module:: This is a Cython file that uses decorators for arguments.
"""
import cython
cython.declare(a = double, x = double, y = double)
def exp(a, x):
""" funtion that uses cython.locals """
cdef int y
y = a**x
return y
In [170]:
!makecython ./src/quad.pyx
In [175]:
%%file ./src/setup.py
from distutils.core import setup, Extension
from Cython.Build import cythonize
#=========================================
# Setup the extensions
#=========================================
sources = [ "./src/cyMath.pyx", "./src/helloCython.pyx",
"./src/cy_math.pyx", "./src/bits.pyx",
"./src/printString.pyx", "./src/quad.pyx"]
#for fileName in sources:
# setup(ext_modules=cythonize(str(fileName)))
map(lambda fileName : setup(ext_modules=cythonize(str(fileName))), sources)
In [176]:
!python ./src/setup.py build_ext --inplace
In [177]:
from src import quad
quad.exp(2,3)
In [154]:
def quadPy(a,x):
return a*(x**2)
In [143]:
%timeit quadPy(2.0, 5.0)
In [ ]:
In [ ]: