Cython -- A Transcompiler Language

Transform Your Python !!

By James Bonanno, Central Ohio Python Presentation, March 2015

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?

  • for making Python faster,
  • for making Python faster in an easy way
  • for wrapping external C and C++
  • making Python accessible to C and C++ (going the other way)

This presentation seeks primarily to discuss ways to transform your Python code and use it in a Python project.

References

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

Why CYTHON?

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

Getting Started:: Cython function types...

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

  • def is importable into python
  • cdef is importable into C, but not python
  • cpdef is importable into both

Getting Started:: Cythonizing a Python function

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

Getting Started:: A Distutils setup.py ...

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()


Overwriting ./src/helloCython.pyx

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


Overwriting ./src/cyMath.pyx

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)


Overwriting ./src/printString.pyx

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


Overwriting ./src/bits.pyx

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)


Overwriting ./src/setup.py

In [46]:
!python ./src/setup.py build_ext --inplace


Compiling ./src/cyMath.pyx because it changed.
Cythonizing ./src/cyMath.pyx
running build_ext
building 'src.cyMath' extension
x86_64-linux-gnu-gcc -pthread -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fPIC -I/usr/include/python2.7 -c ./src/cyMath.c -o build/temp.linux-x86_64-2.7/./src/cyMath.o
x86_64-linux-gnu-gcc -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions -Wl,-Bsymbolic-functions -Wl,-z,relro -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -D_FORTIFY_SOURCE=2 -g -fstack-protector --param=ssp-buffer-size=4 -Wformat -Werror=format-security build/temp.linux-x86_64-2.7/./src/cyMath.o -o /home/james/public_sw/CythonBootstrap/src/cyMath.so
Compiling ./src/helloCython.pyx because it changed.
Cythonizing ./src/helloCython.pyx
running build_ext
building 'src.helloCython' extension
x86_64-linux-gnu-gcc -pthread -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fPIC -I/usr/include/python2.7 -c ./src/helloCython.c -o build/temp.linux-x86_64-2.7/./src/helloCython.o
x86_64-linux-gnu-gcc -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions -Wl,-Bsymbolic-functions -Wl,-z,relro -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -D_FORTIFY_SOURCE=2 -g -fstack-protector --param=ssp-buffer-size=4 -Wformat -Werror=format-security build/temp.linux-x86_64-2.7/./src/helloCython.o -o /home/james/public_sw/CythonBootstrap/src/helloCython.so
running build_ext
Compiling ./src/bits.pyx because it changed.
Cythonizing ./src/bits.pyx
running build_ext
building 'src.bits' extension
x86_64-linux-gnu-gcc -pthread -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fPIC -I/usr/include/python2.7 -c ./src/bits.c -o build/temp.linux-x86_64-2.7/./src/bits.o
x86_64-linux-gnu-gcc -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions -Wl,-Bsymbolic-functions -Wl,-z,relro -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -D_FORTIFY_SOURCE=2 -g -fstack-protector --param=ssp-buffer-size=4 -Wformat -Werror=format-security build/temp.linux-x86_64-2.7/./src/bits.o -o /home/james/public_sw/CythonBootstrap/src/bits.so
Compiling ./src/printString.pyx because it changed.
Cythonizing ./src/printString.pyx
running build_ext
building 'src.printString' extension
x86_64-linux-gnu-gcc -pthread -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fPIC -I/usr/include/python2.7 -c ./src/printString.c -o build/temp.linux-x86_64-2.7/./src/printString.o
x86_64-linux-gnu-gcc -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions -Wl,-Bsymbolic-functions -Wl,-z,relro -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -D_FORTIFY_SOURCE=2 -g -fstack-protector --param=ssp-buffer-size=4 -Wformat -Werror=format-security build/temp.linux-x86_64-2.7/./src/printString.o -o /home/james/public_sw/CythonBootstrap/src/printString.so

In [47]:
from src import helloCython
helloCython.message()


 Hello World ....

 Hello Central Ohio Python User Group ...

 The 614 > 650::True
 Another line 
 The Python version is 2.7.6 (default, Mar 22 2014, 22:59:56) 
[GCC 4.8.2]
 The Cython version is 0.20.1post0
 I hope that you learn something useful . . . .

In [48]:
from src import cyMath
cyMath.cy_fib(100)


0, 1, 

1, 1, 

2, 2, 

3, 3, 

4, 5, 

5, 8, 

6, 13, 

7, 21, 

8, 34, 

9, 55, 

10, 89, 


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]


---------------------------------------------------------------------------
ImportError                               Traceback (most recent call last)
<ipython-input-54-d898240dea5e> in <module>()
      1 from src import bits
----> 2 from bits import *
      3 hexlist = [int(0x01),int(0x02),int(0x04),int(0x08)]
      4 [hex(cy_reflect(item,8)) for item in hexlist]

ImportError: No module named bits

In [55]:
from src import printString
printString.display('123')


49
50
51

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]:
[49, 50, 51]

Now let's see the time difference between a cyfib and pyfib ...



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


Overwriting ./src/cyFib.pyx

Introducing runcython !!

  • Is located on Github
  • Easy installation == pip install runcython
  • Russell91 on Github

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)

NOW THAT IS A CONSIDERABLE SPEEDUP ...


Fibonnaci function shows a factor of over 1500 % Improvement

Let's take a look at disassembly for some reasons for this ....


In [35]:
import dis 
dis.dis(pyfib)


---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-35-d7b58c481ab9> in <module>()
      1 import dis
----> 2 dis.dis(pyfib)

NameError: name 'pyfib' is not defined

In [56]:
import cProfile
cProfile.run('pyfib(1000)')


         2 function calls in 0.000 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.000    0.000 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}


---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-56-13941faa86b0> in <module>()
      1 import cProfile
----> 2 cProfile.run('pyfib(1000)')

/usr/lib/python2.7/cProfile.pyc in run(statement, filename, sort)
     27     try:
     28         try:
---> 29             prof = prof.run(statement)
     30         except SystemExit:
     31             pass

/usr/lib/python2.7/cProfile.pyc in run(self, cmd)
    133         import __main__
    134         dict = __main__.__dict__
--> 135         return self.runctx(cmd, dict, dict)
    136 
    137     def runctx(self, cmd, globals, locals):

/usr/lib/python2.7/cProfile.pyc in runctx(self, cmd, globals, locals)
    138         self.enable()
    139         try:
--> 140             exec cmd in globals, locals
    141         finally:
    142             self.disable()

<string> in <module>()

NameError: name 'pyfib' is not defined

Another Example, with a polynomial this time ...


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

  • $a_0 = 0.1$,
  • $a_1=0.5$
  • $a_2=0.25$.

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))


Overwriting ./src/cyPoly.pyx

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]:
[0.0,
 0.85,
 1.9,
 3.15,
 4.6,
 6.25,
 8.1,
 10.15,
 12.4,
 14.85,
 17.5,
 20.35,
 23.4,
 26.650000000000002,
 30.1,
 33.75,
 37.6,
 41.650000000000006,
 45.9,
 50.35,
 55.0,
 59.85,
 64.9,
 70.15,
 75.6,
 81.25,
 87.10000000000001,
 93.15,
 99.4,
 105.85000000000001,
 112.5,
 119.35000000000001,
 126.4,
 133.65,
 141.10000000000002,
 148.75,
 156.6,
 164.65,
 172.9,
 181.35,
 190.0,
 198.85000000000002,
 207.9,
 217.15,
 226.60000000000002,
 236.25,
 246.10000000000002,
 256.15,
 266.4,
 276.85]

Now's lets do something graphically, like plot a trig function. Let's also use a float/double type.


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)


Overwriting ./src/sineWave.pyx

In [63]:
!makecython ./src/sineWave.pyx

In [64]:
from src import sineWave
import math
angle90 = math.pi/2
sineWave.sinewave(angle90)


Out[64]:
1.0

Now let's looking a data that involves arrays, and look at both python and numpy versions as well.


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)


---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-67-65e45aef2ea0> in <module>()
      4 
      5 x = np.linspace(0,2*np.pi,2000)
----> 6 get_ipython().magic(u"timeit plot(x,np.sin(x),'r')")
      7 
      8 ## %timeit plot(x,sineWave.sinewave(x),'r') <== Why is this a problem ??

/home/james/ipython/IPython/core/interactiveshell.pyc in magic(self, arg_s)
   2306         magic_name, _, magic_arg_s = arg_s.partition(' ')
   2307         magic_name = magic_name.lstrip(prefilter.ESC_MAGIC)
-> 2308         return self.run_line_magic(magic_name, magic_arg_s)
   2309 
   2310     #-------------------------------------------------------------------------

/home/james/ipython/IPython/core/interactiveshell.pyc in run_line_magic(self, magic_name, line)
   2227                 kwargs['local_ns'] = sys._getframe(stack_depth).f_locals
   2228             with self.builtin_trap:
-> 2229                 result = fn(*args,**kwargs)
   2230             return result
   2231 

/home/james/ipython/IPython/core/magics/execution.pyc in timeit(self, line, cell)

/home/james/ipython/IPython/core/magic.pyc in <lambda>(f, *a, **k)
    191     # but it's overkill for just that one bit of state.
    192     def magic_deco(arg):
--> 193         call = lambda f, *a, **k: f(*a, **k)
    194 
    195         if callable(arg):

/home/james/ipython/IPython/core/magics/execution.pyc in timeit(self, line, cell)
   1034             number = 1
   1035             for _ in range(1, 10):
-> 1036                 time_number = timer.timeit(number)
   1037                 worst_tuning = max(worst_tuning, time_number / number)
   1038                 if time_number >= 0.2:

/home/james/ipython/IPython/core/magics/execution.pyc in timeit(self, number)
    130         gc.disable()
    131         try:
--> 132             timing = self.inner(it, self.timer)
    133         finally:
    134             if gcold:

<magic-timeit> in inner(_it, _timer)

NameError: global name 'plot' is not defined

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)


Overwriting ./src/myFunc.pyx

In [69]:
!makecython ./src/myFunc.pyx


In file included from /usr/include/python2.7/numpy/ndarraytypes.h:1761:0,
                 from /usr/include/python2.7/numpy/ndarrayobject.h:17,
                 from /usr/include/python2.7/numpy/arrayobject.h:4,
                 from ./src/myFunc.c:352:
/usr/include/python2.7/numpy/npy_1_7_deprecated_api.h:15:2: warning: #warning "Using deprecated NumPy API, disable it by " "#defining NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION" [-Wcpp]
 #warning "Using deprecated NumPy API, disable it by " \
  ^

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)


---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-70-0db1167d1851> in <module>()
      9 y = myFunc.myfunc(x)
     10 
---> 11 get_ipython().magic(u"timeit plot(x,y,'r')")
     12 
     13 xlim(0,6.28)

/home/james/ipython/IPython/core/interactiveshell.pyc in magic(self, arg_s)
   2306         magic_name, _, magic_arg_s = arg_s.partition(' ')
   2307         magic_name = magic_name.lstrip(prefilter.ESC_MAGIC)
-> 2308         return self.run_line_magic(magic_name, magic_arg_s)
   2309 
   2310     #-------------------------------------------------------------------------

/home/james/ipython/IPython/core/interactiveshell.pyc in run_line_magic(self, magic_name, line)
   2227                 kwargs['local_ns'] = sys._getframe(stack_depth).f_locals
   2228             with self.builtin_trap:
-> 2229                 result = fn(*args,**kwargs)
   2230             return result
   2231 

/home/james/ipython/IPython/core/magics/execution.pyc in timeit(self, line, cell)

/home/james/ipython/IPython/core/magic.pyc in <lambda>(f, *a, **k)
    191     # but it's overkill for just that one bit of state.
    192     def magic_deco(arg):
--> 193         call = lambda f, *a, **k: f(*a, **k)
    194 
    195         if callable(arg):

/home/james/ipython/IPython/core/magics/execution.pyc in timeit(self, line, cell)
   1034             number = 1
   1035             for _ in range(1, 10):
-> 1036                 time_number = timer.timeit(number)
   1037                 worst_tuning = max(worst_tuning, time_number / number)
   1038                 if time_number >= 0.2:

/home/james/ipython/IPython/core/magics/execution.pyc in timeit(self, number)
    130         gc.disable()
    131         try:
--> 132             timing = self.inner(it, self.timer)
    133         finally:
    134             if gcold:

<magic-timeit> in inner(_it, _timer)

NameError: global name 'plot' is not defined

Summary & Conclusions

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

  • Cython enables Python code to be transformed easily
  • The transformed Python code is signficantly faster
  • Wide support and documentation exists for Cython
  • Language has evolved and grown over the past few years with widespread support
  • Usage in Ipython Notebook / Jupyter is now well supported
  • Can be used on a wide variety of programs, ranging from math to logic.

Transform your Python with Cython !!


In [71]:
!python-config --cflags


-I/usr/include/python2.7 -I/usr/include/x86_64-linux-gnu/python2.7  -fno-strict-aliasing -D_FORTIFY_SOURCE=2 -g -fstack-protector --param=ssp-buffer-size=4 -Wformat -Werror=format-security  -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes

In [72]:
!python-config --ldflags


-L/usr/lib/python2.7/config-x86_64-linux-gnu -L/usr/lib -lpthread -ldl  -lutil -lm  -lpython2.7 -Xlinker -export-dynamic -Wl,-O1 -Wl,-Bsymbolic-functions

In [91]:
!ls -a ./src


.	   cyFib.so    helloCython.c	myFunc.pyx	 quad.so
..	   cyMath.c    helloCython.pyx	myFunc.so	 setup.py
bits.c	   cyMath.pyx  helloCython.so	printString.c	 sineWave.pyx
bits.pyx   cyMath.so   __init__.py	printString.pyx  sineWave.so
bits.so    cyPoly.pyx  __init__.py~	printString.so
cyFib.pyx  cyPoly.so   __init__.pyc	quad.pyx

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


Overwriting ./src/quad.pyx

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)


Overwriting ./src/setup.py

In [176]:
!python ./src/setup.py build_ext --inplace


running build_ext
running build_ext
running build_ext
running build_ext
running build_ext
Compiling ./src/quad.pyx because it changed.
Cythonizing ./src/quad.pyx
running build_ext
building 'src.quad' extension
x86_64-linux-gnu-gcc -pthread -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fPIC -I/usr/include/python2.7 -c ./src/quad.c -o build/temp.linux-x86_64-2.7/./src/quad.o
x86_64-linux-gnu-gcc -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions -Wl,-Bsymbolic-functions -Wl,-z,relro -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -D_FORTIFY_SOURCE=2 -g -fstack-protector --param=ssp-buffer-size=4 -Wformat -Werror=format-security build/temp.linux-x86_64-2.7/./src/quad.o -o /home/james/public_sw/CythonBootstrap/src/quad.so

In [177]:
from src import quad

quad.exp(2,3)


---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-177-456320269f24> in <module>()
      1 from src import quad
      2 
----> 3 quad.exp(2,3)

AttributeError: 'module' object has no attribute 'exp'

In [154]:
def quadPy(a,x):
    return a*(x**2)

In [143]:
%timeit quadPy(2.0, 5.0)


The slowest run took 18.05 times longer than the fastest. This could mean that an intermediate result is being cached 
10000000 loops, best of 3: 158 ns per loop

In [ ]:


In [ ]: