Commit e2941f2b authored by gabrieldemarmiesse's avatar gabrieldemarmiesse

Finished 'the first cython program'.

parent adc1aa70
{
"metadata": {
"name": "Cython Magics",
"signature": "sha256:c357b93e9480d6347c6677862bf43750745cef4b30129c5bc53cb879a19d4074"
},
"nbformat": 3,
"nbformat_minor": 0,
"worksheets": [
{
"cells": [
{
"cell_type": "heading",
"level": 1,
"metadata": {},
"source": [
"Cython Magic Functions"
]
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"Loading the extension"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Cython has an IPython extension that contains a number of magic functions for working with Cython code. This extension can be loaded using the `%load_ext` magic as follows:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"%load_ext cython"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 1
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"The %cython_inline magic"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The `%%cython_inline` magic uses `Cython.inline` to compile a Cython expression. This allows you to enter and run a function body with Cython code. Use a bare `return` statement to return values. "
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"a = 10\n",
"b = 20"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 2
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"%%cython_inline\n",
"return a+b"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 3,
"text": [
"30"
]
}
],
"prompt_number": 3
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"The %cython_pyximport magic"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The `%%cython_pyximport` magic allows you to enter arbitrary Cython code into a cell. That Cython code is written as a `.pyx` file in the current working directory and then imported using `pyximport`. You have the specify the name of the module that the Code will appear in. All symbols from the module are imported automatically by the magic function."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"%%cython_pyximport foo\n",
"def f(x):\n",
" return 4.0*x"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 4
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"f(10)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 5,
"text": [
"40.0"
]
}
],
"prompt_number": 5
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"The %cython magic"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Probably the most important magic is the `%cython` magic. This is similar to the `%%cython_pyximport` magic, but doesn't require you to specify a module name. Instead, the `%%cython` magic uses manages everything using temporary files in the `~/.cython/magic` directory. All of the symbols in the Cython module are imported automatically by the magic.\n",
"\n",
"Here is a simple example of a Black-Scholes options pricing algorithm written in Cython. Please note that this example might not compile on non-POSIX systems (e.g., Windows) because of a missing `erf` symbol."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"%%cython\n",
"cimport cython\n",
"from libc.math cimport exp, sqrt, pow, log, erf\n",
"\n",
"@cython.cdivision(True)\n",
"cdef double std_norm_cdf_cy(double x) nogil:\n",
" return 0.5*(1+erf(x/sqrt(2.0)))\n",
"\n",
"@cython.cdivision(True)\n",
"def black_scholes_cy(double s, double k, double t, double v,\n",
" double rf, double div, double cp):\n",
" \"\"\"Price an option using the Black-Scholes model.\n",
" \n",
" s : initial stock price\n",
" k : strike price\n",
" t : expiration time\n",
" v : volatility\n",
" rf : risk-free rate\n",
" div : dividend\n",
" cp : +1/-1 for call/put\n",
" \"\"\"\n",
" cdef double d1, d2, optprice\n",
" with nogil:\n",
" d1 = (log(s/k)+(rf-div+0.5*pow(v,2))*t)/(v*sqrt(t))\n",
" d2 = d1 - v*sqrt(t)\n",
" optprice = cp*s*exp(-div*t)*std_norm_cdf_cy(cp*d1) - \\\n",
" cp*k*exp(-rf*t)*std_norm_cdf_cy(cp*d2)\n",
" return optprice"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 6
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"black_scholes_cy(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 7,
"text": [
"10.327861752731728"
]
}
],
"prompt_number": 7
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"For comparison, the same code is implemented here in pure python."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"from math import exp, sqrt, pow, log, erf\n",
"\n",
"def std_norm_cdf_py(x):\n",
" return 0.5*(1+erf(x/sqrt(2.0)))\n",
"\n",
"def black_scholes_py(s, k, t, v, rf, div, cp):\n",
" \"\"\"Price an option using the Black-Scholes model.\n",
" \n",
" s : initial stock price\n",
" k : strike price\n",
" t : expiration time\n",
" v : volatility\n",
" rf : risk-free rate\n",
" div : dividend\n",
" cp : +1/-1 for call/put\n",
" \"\"\"\n",
" d1 = (log(s/k)+(rf-div+0.5*pow(v,2))*t)/(v*sqrt(t))\n",
" d2 = d1 - v*sqrt(t)\n",
" optprice = cp*s*exp(-div*t)*std_norm_cdf_py(cp*d1) - \\\n",
" cp*k*exp(-rf*t)*std_norm_cdf_py(cp*d2)\n",
" return optprice"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 8
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"black_scholes_py(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 9,
"text": [
"10.327861752731728"
]
}
],
"prompt_number": 9
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Below we see the runtime of the two functions: the Cython version is nearly a factor of 10 faster."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"%timeit black_scholes_cy(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"1000000 loops, best of 3: 319 ns per loop\n"
]
}
],
"prompt_number": 10
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"%timeit black_scholes_py(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"100000 loops, best of 3: 2.28 \u00b5s per loop\n"
]
}
],
"prompt_number": 11
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"External libraries"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Cython allows you to specify additional libraries to be linked with your extension, you can do so with the `-l` flag (also spelled `--lib`). Note that this flag can be passed more than once to specify multiple libraries, such as `-lm -llib2 --lib lib3`. Here's a simple example of how to access the system math library:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"%%cython -lm\n",
"from libc.math cimport sin\n",
"print 'sin(1)=', sin(1)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"sin(1)= 0.841470984808\n"
]
}
],
"prompt_number": 12
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You can similarly use the `-I/--include` flag to add include directories to the search path, and `-c/--compile-args` to add extra flags that are passed to Cython via the `extra_compile_args` of the distutils `Extension` class. Please see [the Cython docs on C library usage](http://docs.cython.org/src/tutorial/clibraries.html) for more details on the use of these flags."
]
}
],
"metadata": {}
}
]
}
{
"metadata": {
"name": "Cython Magics",
"signature": "sha256:c357b93e9480d6347c6677862bf43750745cef4b30129c5bc53cb879a19d4074"
},
"nbformat": 3,
"nbformat_minor": 0,
"worksheets": [
{
"cells": [
{
"cell_type": "heading",
"level": 1,
"metadata": {},
"source": [
"Cython Magic Functions"
]
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"Loading the extension"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Cython has an IPython extension that contains a number of magic functions for working with Cython code. This extension can be loaded using the `%load_ext` magic as follows:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"%load_ext cython"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 1
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"The %cython_inline magic"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The `%%cython_inline` magic uses `Cython.inline` to compile a Cython expression. This allows you to enter and run a function body with Cython code. Use a bare `return` statement to return values. "
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"a = 10\n",
"b = 20"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 2
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"%%cython_inline\n",
"return a+b"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 3,
"text": [
"30"
]
}
],
"prompt_number": 3
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"The %cython_pyximport magic"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The `%%cython_pyximport` magic allows you to enter arbitrary Cython code into a cell. That Cython code is written as a `.pyx` file in the current working directory and then imported using `pyximport`. You have the specify the name of the module that the Code will appear in. All symbols from the module are imported automatically by the magic function."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"%%cython_pyximport foo\n",
"def f(x):\n",
" return 4.0*x"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 4
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"f(10)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 5,
"text": [
"40.0"
]
}
],
"prompt_number": 5
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"The %cython magic"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Probably the most important magic is the `%cython` magic. This is similar to the `%%cython_pyximport` magic, but doesn't require you to specify a module name. Instead, the `%%cython` magic uses manages everything using temporary files in the `~/.cython/magic` directory. All of the symbols in the Cython module are imported automatically by the magic.\n",
"\n",
"Here is a simple example of a Black-Scholes options pricing algorithm written in Cython. Please note that this example might not compile on non-POSIX systems (e.g., Windows) because of a missing `erf` symbol."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"%%cython\n",
"cimport cython\n",
"from libc.math cimport exp, sqrt, pow, log, erf\n",
"\n",
"@cython.cdivision(True)\n",
"cdef double std_norm_cdf_cy(double x) nogil:\n",
" return 0.5*(1+erf(x/sqrt(2.0)))\n",
"\n",
"@cython.cdivision(True)\n",
"def black_scholes_cy(double s, double k, double t, double v,\n",
" double rf, double div, double cp):\n",
" \"\"\"Price an option using the Black-Scholes model.\n",
" \n",
" s : initial stock price\n",
" k : strike price\n",
" t : expiration time\n",
" v : volatility\n",
" rf : risk-free rate\n",
" div : dividend\n",
" cp : +1/-1 for call/put\n",
" \"\"\"\n",
" cdef double d1, d2, optprice\n",
" with nogil:\n",
" d1 = (log(s/k)+(rf-div+0.5*pow(v,2))*t)/(v*sqrt(t))\n",
" d2 = d1 - v*sqrt(t)\n",
" optprice = cp*s*exp(-div*t)*std_norm_cdf_cy(cp*d1) - \\\n",
" cp*k*exp(-rf*t)*std_norm_cdf_cy(cp*d2)\n",
" return optprice"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 6
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"black_scholes_cy(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 7,
"text": [
"10.327861752731728"
]
}
],
"prompt_number": 7
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"For comparison, the same code is implemented here in pure python."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"from math import exp, sqrt, pow, log, erf\n",
"\n",
"def std_norm_cdf_py(x):\n",
" return 0.5*(1+erf(x/sqrt(2.0)))\n",
"\n",
"def black_scholes_py(s, k, t, v, rf, div, cp):\n",
" \"\"\"Price an option using the Black-Scholes model.\n",
" \n",
" s : initial stock price\n",
" k : strike price\n",
" t : expiration time\n",
" v : volatility\n",
" rf : risk-free rate\n",
" div : dividend\n",
" cp : +1/-1 for call/put\n",
" \"\"\"\n",
" d1 = (log(s/k)+(rf-div+0.5*pow(v,2))*t)/(v*sqrt(t))\n",
" d2 = d1 - v*sqrt(t)\n",
" optprice = cp*s*exp(-div*t)*std_norm_cdf_py(cp*d1) - \\\n",
" cp*k*exp(-rf*t)*std_norm_cdf_py(cp*d2)\n",
" return optprice"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 8
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"black_scholes_py(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 9,
"text": [
"10.327861752731728"
]
}
],
"prompt_number": 9
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Below we see the runtime of the two functions: the Cython version is nearly a factor of 10 faster."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"%timeit black_scholes_cy(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"1000000 loops, best of 3: 319 ns per loop\n"
]
}
],
"prompt_number": 10
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"%timeit black_scholes_py(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"100000 loops, best of 3: 2.28 \u00b5s per loop\n"
]
}
],
"prompt_number": 11
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"External libraries"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Cython allows you to specify additional libraries to be linked with your extension, you can do so with the `-l` flag (also spelled `--lib`). Note that this flag can be passed more than once to specify multiple libraries, such as `-lm -llib2 --lib lib3`. Here's a simple example of how to access the system math library:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"%%cython -lm\n",
"from libc.math cimport sin\n",
"print 'sin(1)=', sin(1)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"sin(1)= 0.841470984808\n"
]
}
],
"prompt_number": 12
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You can similarly use the `-I/--include` flag to add include directories to the search path, and `-c/--compile-args` to add extra flags that are passed to Cython via the `extra_compile_args` of the distutils `Extension` class. Please see [the Cython docs on C library usage](http://docs.cython.org/src/tutorial/clibraries.html) for more details on the use of these flags."
]
}
],
"metadata": {}
}
]
}
# cython: infer_types=True
import numpy as np
cimport cython
# "def" can type its arguments but not have a return type. The type of the
# arguments for a "def" function is checked at run-time when entering the
# function.
# We now need to fix a datatype for our arrays. I've used the variable
# DTYPE for this, which is assigned to the usual NumPy runtime
# type info object.
# The arrays f, g and h is typed as "np.ndarray" instances. The only effect
# this has is to a) insert checks that the function arguments really are
# NumPy arrays, and b) make some attribute access like f.shape[0] much
# more efficient. (In this example this doesn't matter though.)
ctypedef fused my_type:
int
......@@ -22,15 +11,7 @@ ctypedef fused my_type:
cpdef naive_convolve_fused_types(my_type [:,:] f, my_type [:,:] g):
if g.shape[0] % 2 != 1 or g.shape[1] % 2 != 1:
raise ValueError("Only odd dimensions on filter supported")
# The "cdef" keyword is also used within functions to type variables. It
# can only be used at the top indentation level (there are non-trivial
# problems with allowing them in other places, though we'd love to see
# good and thought out proposals for it).
#
# For the indices, the "int" type is used. This corresponds to a C int,
# other C types (like "unsigned int") could have been used instead.
# Purists could use "Py_ssize_t" which is the proper Python type for
# array indices.
vmax = f.shape[0]
wmax = f.shape[1]
smax = g.shape[0]
......@@ -50,11 +31,6 @@ cpdef naive_convolve_fused_types(my_type [:,:] f, my_type [:,:] g):
h_np = np.zeros([xmax, ymax], dtype=dtype)
cdef my_type [:,:] h = h_np
# For the value variable, we want to use the same data type as is
# stored in the array, so we use "DTYPE_t" as defined above.
# NB! An important side-effect of this is that if "value" overflows its
# datatype size, it will simply wrap around like in C, rather than raise
# an error like in Python.
cdef my_type value
for x in range(xmax):
for y in range(ymax):
......
# cython: infer_types=True
import numpy as np
cimport cython
# "def" can type its arguments but not have a return type. The type of the
# arguments for a "def" function is checked at run-time when entering the
# function.
# We now need to fix a datatype for our arrays. I've used the variable
# DTYPE for this, which is assigned to the usual NumPy runtime
# type info object.
DTYPE = np.intc
# The arrays f, g and h is typed as "np.ndarray" instances. The only effect
# this has is to a) insert checks that the function arguments really are
# NumPy arrays, and b) make some attribute access like f.shape[0] much
# more efficient. (In this example this doesn't matter though.)
@cython.boundscheck(False)
@cython.wraparound(False)
def naive_convolve_infer_types(int [:,::1] f, int [:,::1] g):
if g.shape[0] % 2 != 1 or g.shape[1] % 2 != 1:
raise ValueError("Only odd dimensions on filter supported")
# The "cdef" keyword is also used within functions to type variables. It
# can only be used at the top indentation level (there are non-trivial
# problems with allowing them in other places, though we'd love to see
# good and thought out proposals for it).
#
# For the indices, the "int" type is used. This corresponds to a C int,
# other C types (like "unsigned int") could have been used instead.
# Purists could use "Py_ssize_t" which is the proper Python type for
# array indices.
vmax = f.shape[0]
wmax = f.shape[1]
smax = g.shape[0]
......@@ -34,14 +17,10 @@ def naive_convolve_infer_types(int [:,::1] f, int [:,::1] g):
tmid = tmax // 2
xmax = vmax + 2*smid
ymax = wmax + 2*tmid
h_np = np.zeros([xmax, ymax], dtype=DTYPE)
cdef int [:,::1] h = h_np
# For the value variable, we want to use the same data type as is
# stored in the array, so we use "DTYPE_t" as defined above.
# NB! An important side-effect of this is that if "value" overflows its
# datatype size, it will simply wrap around like in C, rather than raise
# an error like in Python.
cdef int value
for x in range(xmax):
for y in range(ymax):
......
import numpy as np
# "def" can type its arguments but not have a return type. The type of the
# arguments for a "def" function is checked at run-time when entering the
# function.
# We now need to fix a datatype for our arrays. I've used the variable
# DTYPE for this, which is assigned to the usual NumPy runtime
# type info object.
DTYPE = np.intc
# The arrays f, g and h is typed as "np.ndarray" instances. The only effect
# this has is to a) insert checks that the function arguments really are
# NumPy arrays, and b) make some attribute access like f.shape[0] much
# more efficient. (In this example this doesn't matter though.)
def naive_convolve_memview(int [:,:] f, int [:,:] g):
if g.shape[0] % 2 != 1 or g.shape[1] % 2 != 1:
raise ValueError("Only odd dimensions on filter supported")
# The "cdef" keyword is also used within functions to type variables. It
# can only be used at the top indentation level (there are non-trivial
# problems with allowing them in other places, though we'd love to see
# good and thought out proposals for it).
#
# For the indices, the "int" type is used. This corresponds to a C int,
# other C types (like "unsigned int") could have been used instead.
# Purists could use "Py_ssize_t" which is the proper Python type for
# array indices.
# We don't need to check for the type of NumPy array here because
# a check is already performed when calling the function.
cdef int x, y, s, t, v, w, s_from, s_to, t_from, t_to
cdef int vmax = f.shape[0]
cdef int wmax = f.shape[1]
cdef int smax = g.shape[0]
......@@ -31,18 +17,10 @@ def naive_convolve_memview(int [:,:] f, int [:,:] g):
cdef int tmid = tmax // 2
cdef int xmax = vmax + 2*smid
cdef int ymax = wmax + 2*tmid
h_np = np.zeros([xmax, ymax], dtype=DTYPE)
cdef int [:,:] h = h_np
cdef int x, y, s, t, v, w
# It is very important to type ALL your variables. You do not get any
# warnings if not, only much slower code (they are implicitly typed as
# Python objects).
cdef int s_from, s_to, t_from, t_to
# For the value variable, we want to use the same data type as is
# stored in the array, so we use "DTYPE_t" as defined above.
# NB! An important side-effect of this is that if "value" overflows its
# datatype size, it will simply wrap around like in C, rather than raise
# an error like in Python.
cdef int value
for x in range(xmax):
for y in range(ymax):
......
......@@ -24,6 +24,8 @@ def naive_convolve_types(f, g):
# other C types (like "unsigned int") could have been used instead.
# Purists could use "Py_ssize_t" which is the proper Python type for
# array indices.
cdef int x, y, s, t, v, w, s_from, s_to, t_from, t_to
cdef int vmax = f.shape[0]
cdef int wmax = f.shape[1]
cdef int smax = g.shape[0]
......@@ -33,11 +35,9 @@ def naive_convolve_types(f, g):
cdef int xmax = vmax + 2*smid
cdef int ymax = wmax + 2*tmid
h = np.zeros([xmax, ymax], dtype=DTYPE)
cdef int x, y, s, t, v, w
# It is very important to type ALL your variables. You do not get any
# warnings if not, only much slower code (they are implicitly typed as
# Python objects).
cdef int s_from, s_to, t_from, t_to
# For the value variable, we want to use the same data type as is
# stored in the array, so we use "DTYPE_t" as defined above.
# NB! An important side-effect of this is that if "value" overflows its
......
......@@ -133,13 +133,13 @@ The first Cython program
The code below does 2D discrete convolution of an image with a filter (and I'm
sure you can do better!, let it serve for demonstration purposes). It is both
valid Python and valid Cython code. I'll refer to it as both
:file:`convolve_py.py` for the Python version and :file:`convolve1.pyx` for the
:file:`convolve_py.py` for the Python version and :file:`convolve_cy.pyx` for the
Cython version -- Cython uses ".pyx" as its file suffix.
.. literalinclude:: ../../examples/userguide/convolve_py.py
:linenos:
This should be compiled to produce :file:`yourmod.so` (for Linux systems). We
This should be compiled to produce :file:`convolve_cy.so` (for Linux systems). We
run a Python session to test both the Python version (imported from
``.py``-file) and the compiled Cython module.
......@@ -153,8 +153,8 @@ run a Python session to test both the Python version (imported from
array([[1, 1, 1],
[2, 2, 2],
[1, 1, 1]])
In [4]: import convolve1
In [4]: convolve1.naive_convolve(np.array([[1, 1, 1]], dtype=np.int),
In [4]: import convolve_cy
In [4]: convolve_cy.naive_convolve(np.array([[1, 1, 1]], dtype=np.int),
... np.array([[1],[2],[1]], dtype=np.int))
Out [4]:
array([[1, 1, 1],
......@@ -164,13 +164,17 @@ run a Python session to test both the Python version (imported from
In [12]: f = np.arange(N*N, dtype=np.int).reshape((N,N))
In [13]: g = np.arange(81, dtype=np.int).reshape((9, 9))
In [19]: %timeit -n2 -r3 convolve_py.naive_convolve(f, g)
2 loops, best of 3: 1.86 s per loop
In [20]: %timeit -n2 -r3 convolve1.naive_convolve(f, g)
2 loops, best of 3: 1.41 s per loop
422 ms ± 2.06 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [20]: %timeit -n2 -r3 convolve_cy.naive_convolve(f, g)
342 ms ± 1.39 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
There's not such a huge difference yet; because the C code still does exactly
what the Python interpreter does (meaning, for instance, that a new object is
allocated for each number used). Look at the generated html file and see what
allocated for each number used). You can look at the Python interaction
and the generated C code by using `-a` when calling Cython from the command
line, `%%cython -a` when using a Jupyter Notebook, or by using
`cythonize('convolve_cy.pyx', annotate=True)` when using a `setup.py`.
Look at the generated html file and see what
is needed for even the simplest statements you get the point quickly. We need
to give Cython more information; we need to add types.
......@@ -358,9 +362,16 @@ mode).
Where to go from here?
======================
* Since there is no Python interaction in the loops, it is possible with Cython
to release the GIL and use multiple cores easily. To learn how to do that,
you can see :ref:`using parallelism in Cython <parallel>`.
* If you want to learn how to make use of `BLAS <http://www.netlib.org/blas/>`_
or `LAPACK <http://www.netlib.org/lapack/>`_ with Cython, you can watch
`the presentation of Ian Henriksen at SciPy 2015
<https://www.youtube.com/watch?v=R4yB-8tB0J0&t=693s&ab_channel=Enthought>`_.
The future
============
==========
These are some points to consider for further development. All points listed
here has gone through a lot of thinking and planning already; still they may
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment