-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
063eac7
commit 481facc
Showing
1 changed file
with
193 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
{ | ||
"cells": [ | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": { | ||
"slideshow": { | ||
"slide_type": "slide" | ||
} | ||
}, | ||
"source": [ | ||
"# Optimisation - Cython\n", | ||
"## Martin Robinson\n", | ||
"## Oct 2019" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": { | ||
"slideshow": { | ||
"slide_type": "slide" | ||
} | ||
}, | ||
"source": [ | ||
"# Using lower-level languages\n", | ||
"\n", | ||
"- Interpreted languages are fundamentially speed-limited when they only consider *type* at run-time.\n", | ||
"- e.g. consider what happens with the types of the variables in the following function\n", | ||
"```python\n", | ||
"def norm(arg_list, p):\n", | ||
" sum = 0 # sum is an int here\n", | ||
" for x in arg_list: # type of x depends on input container\n", | ||
" sum += abs(x)**p # type of rhs depends on both x and p, sum could *change* type here\n", | ||
" return sum**(1.0/p) # return value is probably float due to 1.0\n", | ||
"```\n", | ||
"- how much memory to allocate for sum? does this memory need to be re-allocated during the loop? are conversion routines between types required during the loop?" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": { | ||
"slideshow": { | ||
"slide_type": "slide" | ||
} | ||
}, | ||
"source": [ | ||
"- compare to equivilant C++ code\n", | ||
"```cpp\n", | ||
"float norm(std::vector<float>& arg_list, float p) {\n", | ||
" float sum = 0.0f;\n", | ||
" for (size_t i = 0; i < arg_list.size(); ++i) {\n", | ||
" sum += std::pow(std::abs(arg_list), p);\n", | ||
" }\n", | ||
" return std::pow(sum, 1.0f/p);\n", | ||
"}\n", | ||
"```\n", | ||
"- compiler can pre-allocate the stack size because the sizes of local variables known, no type conversions neccessary\n", | ||
"- compiler can generate efficient machine code because the programmer has provided more information (i.e. types)\n", | ||
" " | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"# \"Compiling\" Python code\n", | ||
"\n", | ||
"- All python implementations (CPython, PyPy, IronPython) compile to *bytecode*, which is then either interpreted at run-time, or perhaps further compiled to native machine code\n", | ||
"- Implementations that compile to native machine code usually implement something close to normal python, but with restrictions or additions that alter the nature of the language. These include:\n", | ||
" - Cython (Python-to-C)\n", | ||
" - Nuitka (Python-to-C++)\n", | ||
" - Numba (Python-to-LLVM IR)" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"# \"Wrapping\" C and C++ for use in Python\n", | ||
"\n", | ||
"- the compilers in the previous slide implement an altered version of python, yet another language to learn!\n", | ||
"- If your already comfortable with C, C++ or Fortran, why not use this directly and write a *wrapper* to call from Python?\n", | ||
"- " | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"# Cython" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"# Your first Cython program" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"```python\n", | ||
"def norm(a, p):\n", | ||
" s = 0\n", | ||
" x_max = a.shape[0]\n", | ||
" y_max = a.shape[1]\n", | ||
" for i in range(x_max):\n", | ||
" for j in range(y_max):\n", | ||
" s += abs(a[i, j])**p\n", | ||
" return s**(1.0/p)\n", | ||
"\n", | ||
"```" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"# Manual compilation" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"# Examining the generated code" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"# Adding types" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"# memoryviews" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"# Tuning indexing further" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"```python\n", | ||
"from libc.math cimport abs\n", | ||
"cimport cython\n", | ||
"\n", | ||
"@cython.boundscheck(False) # Deactivate bounds checking\n", | ||
"@cython.wraparound(False) # Deactivate negative indexing.\n", | ||
"@cython.cdivision(True) # Deactivate normal python division checking\n", | ||
"cdef double norm(double [:, :] a, int p):\n", | ||
" cdef double s = 0\n", | ||
" cdef Py_ssize_t x_max = a.shape[0]\n", | ||
" cdef Py_ssize_t y_max = a.shape[1]\n", | ||
" for i in range(x_max):\n", | ||
" for j in range(y_max):\n", | ||
" s += abs(a[i, j])**p\n", | ||
" return s**(1.0/p)\n", | ||
"```" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"# Packaging Cython programs" | ||
] | ||
} | ||
], | ||
"metadata": { | ||
"language_info": { | ||
"name": "python", | ||
"pygments_lexer": "ipython3" | ||
} | ||
}, | ||
"nbformat": 4, | ||
"nbformat_minor": 2 | ||
} |