Python C++
Python C++
Based partially on a talk by Stéfan van der Walt This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 License.
[xkcd]
Easy to use/Flexibility
...
C/C++
Fortran
Speed/Complexity
September 6, 2017 Python meets C/C++ Page 3
Department of Physics
Easy to use/Flexibility
...
C/C++
Fortran
Speed/Complexity
September 6, 2017 Python meets C/C++ Page 3
Department of Physics
Easy to use/Flexibility
...
C/C++
Fortran
Speed/Complexity
September 6, 2017 Python meets C/C++ Page 3
Department of Physics
Table of Contents
I Introduction
I Why should I care?
I When should I consider something else?
I cython – a hybrid programming language/compiler
I Speed-up examples
I Standard Template Library
I Classes
I Exceptions
I SWIG (and other wrappers)
Bad code stays bad code! – Better clean Do not expect miracles! – You have to
it up than trying to overpaint it! master two languages!
Pure Python
»»» a = [1,2,3,4,5,6,7,8]
»»» sum(a)
36
NumPy’s C API
ndarray typedef struct PyArrayObject {
PyObject_HEAD
char *data;
int nd;
npy_intp *dimensions;
npy_intp *strides;
PyObject *base;
PyArray_Descr *descr;
int flags;
PyObject *weakreflist;
} PyArrayObject ;
Fibonacci (Cython)
def fib(int n):
cdef int i,a,b
a,b = 1,1
for i in range(n):
a,b = a+b,a
return a
Fibonacci (Cython)
def fib(int n):
cdef int i,a,b
a,b = 1,1
for i in range(n):
a,b = a+b,a
return a
I A few (simple) modifications can easily change the CPU time by a factor of O(100)
Command python setup.py build_ext --inplace creates for each .pyx file a .c/.cpp file,
compiles it to an executable (in the build directory of the corresponding OS/architecture/Python
version) and compiles a .so file (or a .pxd if you are using Windows)
Further options for cythonize via help explorable
Integrate
from math import sin,exp
def f(double x):
return sin(x)*exp(-x)
def integrate(double a,double b,int N):
cdef double dx,s
cdef int i
dx = (b-a)/N
s = 0.0
for i in range(N):
s += f(a+(i+0.5)*dx)
return s*dx
September 6, 2017 Python meets C/C++ Page 17
Department of Physics
Integrate
from math import sin,exp
cdef double f(double x):
return sin(x)*exp(-x)
def integrate(double a,double b,int N):
cdef double dx,s
cdef int i
dx = (b-a)/N
s = 0.0
for i in range(N):
s += f(a+(i+0.5)*dx)
return s*dx
September 6, 2017 Python meets C/C++ Page 17
Department of Physics
Integrate
from math import sin,exp
cpdef double f(double x):
return sin(x)*exp(-x)
def integrate(double a,double b,int N):
cdef double dx,s
cdef int i
dx = (b-a)/N
s = 0.0
for i in range(N):
s += f(a+(i+0.5)*dx)
return s*dx
September 6, 2017 Python meets C/C++ Page 17
Department of Physics
Integrate
from libc.math cimport sin,exp
cpdef double f(double x):
return sin(x)*exp(-x)
def integrate(double a,double b,int N):
cdef double dx,s
cdef int i
dx = (b-a)/N
s = 0.0
for i in range(N):
s += f(a+(i+0.5)*dx)
return s*dx
September 6, 2017 Python meets C/C++ Page 17
Department of Physics
Integrate
from libc.math cimport sin,exp
cpdef double f(double x):
return sin(x)*exp(-x)
def integrate(double a,double b,int N):
cdef double dx,s
cdef Py_ssize_t i
dx = (b-a)/N
s = 0.0
for i in range(N):
s += f(a+(i+0.5)*dx)
return s*dx
September 6, 2017 Python meets C/C++ Page 17
Department of Physics
I C/C++ library can be imported via from libc/libcpp.<module> cimport <name> (see
later)
I Using C++ functions can lead to a huge speed-up
I Try to do as much as you can in the C-layer
STL Containers
An often used feature of C++ are the Standard Template Library containters (e.g. std::vector,
std::map, etc.)
Object holders with specific memory access structure, e.g.
I std::vector allows to access any element
I std::list only allows to access elements via iteration
I std::map represents an associative container with a key and a mapped values
STL Containers
An often used feature of C++ are the Standard Template Library containters (e.g. std::vector,
std::map, etc.)
. . . and Cython knows how to treat them!
STL Containers
An often used feature of C++ are the Standard Template Library containters (e.g. std::vector,
std::map, etc.)
A few remarks!
I iterators (e.g. it) can be used ⇒ dereferencing with dereference(it) and
incrementing/decrementing with preincrement (i.e. ++it), postincrement (i.e. it++),
predecrement (i.e. --it) and postdecrement (i.e. it--) from cython.operator
I Be careful with performance! ⇒ performance lost due to shuffling of data
I More indepth information can be found directly in the corresponding sections of the cython
code https://github.com/cython/cython/tree/master/Cython/Includes/libcpp
I C++11 containters (like std::unordered_map) are partially implemented
Exceptions/Errors
In terms of exception and error handling three different cases need to be considered:
I Raising of a Python error in cython code ⇒ return values make it impossible to raise
properly Python errors (Warning message, but continuing)
I Handling of error codes from pure C functions
I Raising of a C++ exception in C++ code used in cython ⇒ C++ exception terminates – if
not caught – program
Errors in Python
Python Error
cpdef int raiseError():
raise RuntimeError("A problem")
return 1
Errors in Python
Python Error
cpdef int raiseError():
raise RuntimeError("A problem")
return 1
⇒ Just prints a warning (and worse gives an ambigious return value)
Python Error
cpdef int raiseError() except *:
raise RuntimeError("A problem")
return 1
Errors in C
C does not know exceptions like Python or C++. If errors should be caught, it is usually done via
dedicated return values of functions which cannot appear in a regular function call.
Use the except statement to tell cython about this value
C Error
cpdef int raiseException() except -1:
return -1
Exceptions in C++
[xkcd]
Exceptions in C++
. . . and how to tackle them!
I cdef <C++ function>() except + C++ −→ Python
⇒ translates a C++ exception into a Python bad_alloc → MemoryError
error according to the right-hand scheme bad_cast → TypeError
I cdef <C++ function>() except domain_error → ValueError
invalid_argument → ValueError
+<Python Error> e.g. MemoryError ⇒
ios_base::failure → IOError
translates every thrown C++ exception into out_of_range → IndexError
a MemoryError overflow_error → OverflowError
range_error → ArithmeticError
I cdef <C++ function>() except
underflow_error → ArithmeticError
+<function raising Python error> ⇒ (all others) → RuntimeError
runs the indicated function if the C++
function throws any exception. If <function
raising Python error> does not raise an
error, a RuntimeError will be raised.
September 6, 2017 Python meets C/C++ Page 23
Department of Physics
Classes
Classes are a common feature of Python and C++
There are two aspects when dealing with cython:
I Defining classes containing C++ code in cython
I C++ classes integrated into Python
integrate(Poly(),0.0,2.0,1000)
⇒ Speed lost with respect to definition in cython, but still faster than a pure Python
implementation
Automatic Wrappers
. . . since not everybody likes to write lines of error-prone code
I SWIG
I boost::python
I ctypes
I ...
SWIG
SWIG: Simplified Wrapper and Interface Generator
I Generic Wrapper for C/C++ to script-like languages
I R
I Perl
I Ruby
I Tcl
I PHP5
I Java
I . . . and Python
I Pretty old – created in 1995 by Dave Beazley
I Current version is 3.0.12
SWIG – in a Nutshell
SWIG – in a Nutshell
SWIG – in a Nutshell
SWIG – in a Nutshell
SWIG – in a Nutshell
Distutils (setup.py)
from distutils.core import setup, Extension
extension_mod = Extension("_<name>",
["<name_wrap>.cxx",
"<source1>.cpp",
"<source2>.cpp","..."],
language="c++")
setup(name = "_<name>", ext_modules=[extension_mod])
I To be build extension needs a different name than the module set up by switch ⇒ Avoid
name conflicts
I Language option only for C++
I python setup.py build_ext --inplace
September 6, 2017 Python meets C/C++ Page 31
Department of Physics
Conclusion
I Interfacing Python with C/C++ is – or
better – can be a way to create powerful
code
I cython and SWIG are two nice tools to
do so
I . . . but always make the interfacing
maintainable/useful/etc. i.e. not a British
train door
I And it’s all about finding the sweet spot!
Python + C/C++
Python
Compilation
The End!
[xkcd]
September 6, 2017 Python meets C/C++ Page 35
Department of Physics
References
1. Stéfan van der Walt, Speeding up scientific Python code using Cython, Advanced Scientific
Programming in Python, 2013 (Zurich) & 2014 (Split)
2. Stefan Behnel et al., Cython tutorial, Proceedings of the 8th Python in Science Conference (SciPy 2009)
⇒ based on older cython version, but the main reference of cython
3. Dave Beazley, Swig Master Class, PyCon’2008
4. http://docs.cython.org/src/tutorial/
5. http://www.swig.org
Backup
Department of Physics
Exceptions in C++
Examples
Two C++ functions void raiseException() and void raiseBadAlloc() defined in
except_cy.h
Exception Example 1
cdef extern from ’except_cy.h’
cdef void raiseException() except +
def tryIt():
try:
raiseException()
except RuntimeError as e:
print(e)
Exceptions in C++
Examples
Two C++ functions void raiseException() and void raiseBadAlloc() defined in
except_cy.h
Exception Example 2
cdef extern from ’except_cy.h’
cdef void raiseException() except +MemoryError
def tryIt():
try:
raiseException()
except RuntimeError as e:
print(e)
Exceptions in C++
Examples
Two C++ functions void raiseException() and void raiseBadAlloc() defined in
except_cy.h
Exception Example 3
cdef extern from ’except_cy.h’
cdef void raiseBadAlloc() except +
def tryIt():
try:
raiseBadAlloc()
except RuntimeError as e:
print(e)
Exceptions in C++
Examples
Two C++ functions void raiseException() and void raiseBadAlloc() defined in
except_cy.h
Exception Example 4
cdef extern from ’except_cy.h’
cdef void raiseBadAlloc() except +
def tryIt():
try:
raiseBadAlloc()
except MemoryError as e:
print(e)
Exceptions in C++
Examples
Two C++ functions void raiseException() and void raiseBadAlloc() defined in
except_cy.h
Exception Example 5
cdef void raise_py_error() except *:
raise MemoryError("Problem")
cdef extern from ’except_cy.h’:
cdef void raiseBadAlloc() except +raise_py_error
def tryIt():
try:
raiseBadAlloc()
except MemoryError as e:
print(e)
Exceptions in C++
Examples
Two C++ functions void raiseException() and void raiseBadAlloc() defined in
except_cy.h
Exception Example 6
cdef void raise_py_error() except *:
pass
cdef extern from ’except_cy.h’:
cdef void raiseBadAlloc() except +raise_py_error
def tryIt():
try:
raiseBadAlloc()
except MemoryError as e:
print(e)
Rectangle.h
namespace shapes {
class Rectangle {
public:
int x0, y0, x1, y1;
Rectangle(int x0, int y0, int x1, int y1);
∼Rectangle(); // destructor
int getLength();
int getHeight();
int getArea();
void move(int dx, int dy);
};
}
Rectangle.cpp
#include "Rectangle.h"
#include <iostream>
using namespace shapes;
Rectangle::Rectangle(int X0, int Y0, int X1, int Y1) {
x0 = X0;
y0 = Y0;
x1 = X1;
y1 = Y1;
std::cout « "Here I am" « std::endl;}
Rectangle::∼Rectangle() {
std::cout « "Byebye" « std::endl;}
...
Interface file
...
%include "std_vector.i"
%include "std_string.i"
...
%template(dVector) std::vector<double>;
%template(rectVector) std::vector<Rectangle*>;
...
Interface file
...
%include "exception.i"
...
%exceptionclass ShapeError;
%exception *::whine {
try {
$action
} catch(ShapeError & e) {
ShapeError *ecopy = new ShapeError(e);
PyObject *err = SWIG_NewPointerObj(ecopy, SWIGTYPE_p_ShapeError, 1);
PyErr_SetObject(SWIG_Python_ExceptionType(SWIGTYPE_p_ShapeError), err);
SWIG_fail;
}
}
September 6, 2017 Python meets C/C++ Page 42
Department of Physics
C++ classes
class Shape {
public:
...
void virtual printInfo(); // Prints "Shape"
};
class Rectangle : public Shape {
public:
...
void printInfo(); // Prints "Rectangle"
};
C++ classes
cdef extern from "Rectangle.h" namespace "shapes":
cdef cppclass Shape:
Shape() except +
void printInfo()
cdef cppclass Rectangle(Shape):
Rectangle(int, int, int, int) except +
...
void printInfo() # causes problems
...
A
B*A B
C++ code
Rectangle operator*(Rectangle& rhs){
return Rectangle(x0,y0,rhs.x1,rhs.y1);
};
rect.pyx
# to expose it to cython
Rectangle operator*(Rectangle)
Arrays
Arrays in cython are usually treated via typed memoryviews (e.g. double[:,:] means a
two-dimensional array of doubles, i.e. compatible with e.g. np.ones((3,4)))
Further you can specify which is the fastest changing index by :1, e.g.
I double[::1,:,:] is a F-contiguous three-dimensional array
I double[:,:,::1] is a C-contiguous three-dimensional array
I double[:,::1,:] is neither F- nor C-contiguous
For example a variable double[:,::1] a has as NumPy arrays variables like shape and size
and the elements can be accessed by a[i,j]
But be aware: NumPy is already heavily optimised, so do not to reinvent the wheel!