diff --git a/.gitignore b/.gitignore index 5b28155afe46a26cdd2cfa97e0ac010272e8d315..552d0b9f4e896269d2358c5d32aa98372b652ef2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,11 @@ # directories -__pycache__ +#__pycache__ .unison .ipynb_checkpoints # files -*.so -*.o +#*.so +#*.o *.lprof *.tmp diff --git a/examples/cython/c_interface/README.md b/examples/cython/c_interface/README.md index af8026d8f4da2950dbd46c8aced1f611acaa97f3..ef817270a6664451b7b2c8791f9e391d256c4fc2 100644 --- a/examples/cython/c_interface/README.md +++ b/examples/cython/c_interface/README.md @@ -2,11 +2,10 @@ ## Features -* Python module "foobar" lives in the directory "foobar" (has `__index__.py`) +* Python module "foobar.hello" lives in the directory "foobar" (has `__index__.py`) * `c_hello.c` contains a hello-world implementation in C -* `hello.pyx` provides the sub-module 'foobar.hello' and contains the Python interface written in Cython +* `hello.pyx` provides the module 'foobar.hello' and contains the Python interface written in Cython * `setup.py` contains code to compile the extension, both the source files are compiled into the same shared object -* `setup.py` demonstrates how to implement a clean command ## Usage @@ -15,5 +14,5 @@ `python -c "from foobar import hello; hello.say_hello()"` * or install the package, and run the extension from anywhere `python setup.py install --user` -* alternatively, run `test_foobar.py` to call the compiled code example +* run `test_foobar.py` to call the compiled code example diff --git a/examples/cython/c_interface/setup.py b/examples/cython/c_interface/setup.py index 15a0b895ac5fd8a1ed57d7f3d3b249c297f49ce1..c8db1139946062e428e5290c69c34300624ab92b 100644 --- a/examples/cython/c_interface/setup.py +++ b/examples/cython/c_interface/setup.py @@ -2,7 +2,8 @@ import os from setuptools import setup, Extension ext = Extension("foobar.hello", - sources=["foobar/hello.pyx", "foobar/c_hello.c"]) + sources=["foobar/hello.pyx", "foobar/c_hello.c"] +) setup(name="foobar", ext_modules=[ext], diff --git a/examples/cython/c_interface_shared_object/README.md b/examples/cython/c_interface_shared_object/README.md index dcffb69d5387d4c9e03b82f267d1fa5dcd8066f9..8eb9e405f08eda20de461c84fed1e133334f8f46 100644 --- a/examples/cython/c_interface_shared_object/README.md +++ b/examples/cython/c_interface_shared_object/README.md @@ -6,10 +6,10 @@ * `hello.pyx` contains the Python interface written in Cython * `c_hello.c` contains a hello-world implementation in C, however, in contrast to the previous simple example it is located in a different library (.so, - shared object) in the directory `libhello`. Use `make` to generate the - shared object. + shared object) in the directory `libhello`. + Use `make` to generate the shared object in that directory. * `setup.py` contains code to compile the extension, demonstrating how to link - the shared object in a stable way + the shared object in a stable way. ## Usage diff --git a/examples/cython/c_interface_shared_object/libhello/Makefile b/examples/cython/c_interface_shared_object/libhello/Makefile index e0f11f198f84ae46ca0fecb5475f7eff7041d08e..46f10f9fccdb0bfbe4188110794e11d9b4d8af46 100644 --- a/examples/cython/c_interface_shared_object/libhello/Makefile +++ b/examples/cython/c_interface_shared_object/libhello/Makefile @@ -1,8 +1,8 @@ all: libhello.so libhello.so: c_hello.c - gcc -Wall -fPIC -c c_hello.c - gcc -shared -o $@ c_hello.o + $(CC) -Wall -fPIC -c c_hello.c + $(CC) -shared -o $@ c_hello.o .PHONY: clean clean: diff --git a/examples/cython/c_interface_shared_object/setup.py b/examples/cython/c_interface_shared_object/setup.py index f19d9cf7f838d34a9529768f191c9026841cea34..1646a169537267e705cebf210d1d3c82b2fe474c 100644 --- a/examples/cython/c_interface_shared_object/setup.py +++ b/examples/cython/c_interface_shared_object/setup.py @@ -1,7 +1,6 @@ import os from setuptools import setup, Extension - libhello_dir = os.path.abspath("./libhello") # We need to specify the location of the include file. @@ -19,7 +18,8 @@ ld_flags.append("-lhello") # link libhello ext = Extension("wrap_libhello.hello", sources=["wrap_libhello/hello.pyx"], extra_link_args=ld_flags, - include_dirs=include_dirs) + include_dirs=include_dirs +) setup(name="wrap_libhello", ext_modules=[ext], diff --git a/examples/cython/c_numpy/README.md b/examples/cython/c_numpy/README.md index c3a6dbe1f61688c7e4e8be216e546188a552df9e..6ccd25cefc09ec74b9301aee8ae45dd529dfd05a 100644 --- a/examples/cython/c_numpy/README.md +++ b/examples/cython/c_numpy/README.md @@ -2,7 +2,7 @@ ## Features -* Python module "ctonumpy" lives in the directory "ctonumpy" (has `__init__.py`) +* Python module "ctonumpy.cube" lives in the directory "ctonumpy" (has `__init__.py`) * `c_cube.c` contains a OMP parallel loop-based computation of the third power of an array * `cube.pyx` contains the Python interface written in Cython * `setup.py` contains code to compile the extension with optimizing compiler flags diff --git a/examples/cython/c_numpy/setup.py b/examples/cython/c_numpy/setup.py index 57009d0eab05a6058dd7fd92544ad1e3486e327d..0ee82e9b335e171a5d8614d6f37f47664e5fa76e 100644 --- a/examples/cython/c_numpy/setup.py +++ b/examples/cython/c_numpy/setup.py @@ -16,14 +16,15 @@ c_flags.append("-fopt-info") include_dirs = [] include_dirs.append(np.get_include()) -# extra link flags are possible as well; we leave them empty here -ld_flags = [] +# extra link flags are possible as well; here we use the same as for compilation to link to libgomp +ld_flags = c_flags ext = Extension("ctonumpy.cube", sources=["ctonumpy/cube.pyx", "ctonumpy/c_cube.c"], extra_compile_args=c_flags, extra_link_args=ld_flags, - include_dirs=[np.get_include()]) + include_dirs=[np.get_include()] +) setup(name="ctonumpy", ext_modules=[ext], diff --git a/examples/f2py/README.md b/examples/f2py/README.md new file mode 100644 index 0000000000000000000000000000000000000000..1738f5f7293c3fc0b2ea10e344a60cebc722291c --- /dev/null +++ b/examples/f2py/README.md @@ -0,0 +1,9 @@ +# Minimal example on how-to interface with Fortran code using 'f2py' + +Options: + +* compile on the command line using `f2py`, cf. 'compile_with_f2py.sh' +* compile using 'setup.py', using the command `python setup.py build_ext --inplace` + +Run the module using 'test_fibonacci.py'. + diff --git a/examples/f2py/fibonacci.py b/examples/f2py/test_fibonacci.py similarity index 100% rename from examples/f2py/fibonacci.py rename to examples/f2py/test_fibonacci.py diff --git a/examples/pybind11/README.md b/examples/pybind11/README.md new file mode 100644 index 0000000000000000000000000000000000000000..4a8cf22df67a7d61760b0b0310b389a05a11fe6f --- /dev/null +++ b/examples/pybind11/README.md @@ -0,0 +1,5 @@ +# Minimal example showing how to wrap C/C++ code into Python/Numpy using pybind11 + +* you may need to run `pip install --user pybind11` if not yet installed +* run `python test_pybex.py` to see the 'pybex' module in action + diff --git a/examples/pybind11/setup.py b/examples/pybind11/setup.py index 5ef0a12dcc7c3f782aef80548d090f19774d5e8f..402e322b22a05d463ba70b576654cdb7868711e7 100644 --- a/examples/pybind11/setup.py +++ b/examples/pybind11/setup.py @@ -2,10 +2,15 @@ from setuptools import setup # run `pip install --user pybind11` if not yet installed from pybind11.setup_helpers import Pybind11Extension, build_ext +# extra compile/link args may be injected, e.g. here for optimization and openmp with gcc +extra_gcc_args=['-O3', '-march=native', '-fopenmp'] + ext_modules = [ Pybind11Extension( "pybex", ["src/pybex.cpp",], + extra_compile_args=extra_gcc_args, + extra_link_args=extra_gcc_args ), ] diff --git a/examples/pybind11/src/pybex.cpp b/examples/pybind11/src/pybex.cpp index 3700913d4ffa2fb975d3db274bfedaca5e2cdc89..8f1f90b7ccd8d504b0e111b47c297f26f0f59b2e 100644 --- a/examples/pybind11/src/pybex.cpp +++ b/examples/pybind11/src/pybex.cpp @@ -1,29 +1,45 @@ // pybind11 example module 'pybex' +#include <vector> +#include <numeric> +#include <functional> + #include <pybind11/pybind11.h> #include <pybind11/numpy.h> -namespace py = pybind11; // C/C++ implementation of the function to be wrapped void c_cube(const double * v_in, double * v_out, size_t n_elem) { + #pragma omp parallel for simd default(none) shared(v_in, v_out, n_elem) for (size_t i=0; i<n_elem; ++i) { v_out[i] = v_in[i] * v_in[i] * v_in[i]; } } + +namespace py = pybind11; + // wrapper function, accepting a NumPy array as input and returning a NumPy array -py::array_t<double> py_cube(py::array_t<double, py::array::c_style> v_in) +py::array_t<double> py_cube(py::array_t<double, py::array::c_style> np_in) { - // allocate buffer to be returned as a NumPy array - auto v_out = py::array_t<double>(v_in.size()); - double * v_out_ptr = (double*) v_out.request().ptr; + // obtain information about the input array + py::buffer_info pybuf_in = np_in.request(); + auto shape = pybuf_in.shape; + + // create output NumPy array of the same size and dimension + py::array_t<double, py::array::c_style> np_out(shape); - c_cube(v_in.data(), v_out_ptr, v_in.size()); + // call C/C++ function with proper arguments + double * pybuf_in_ptr = (double*) pybuf_in.ptr; + double * pybuf_out_ptr = (double*) np_out.request().ptr; + size_t count = std::accumulate(shape.begin(), shape.end(), 1, std::multiplies<size_t>()); + c_cube(pybuf_in_ptr, pybuf_out_ptr, count); - return v_out; + return np_out; } + +// define Python module, expose py_cube function as "cube" to python PYBIND11_MODULE(pybex, m) { m.doc() = "pybind11 example module"; // module docstring diff --git a/examples/pybind11/test_pybex.py b/examples/pybind11/test_pybex.py new file mode 100755 index 0000000000000000000000000000000000000000..932fa156e6a9ac5589b9f7ef607782f7c2f14efa --- /dev/null +++ b/examples/pybind11/test_pybex.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python + +import numpy as np +from time import time +from pybex import cube + + +def test_pybex_cube(N=384): + v = np.full((N, N, N), fill_value=2.0) + + # (1) compute the result using our C extension + t0 = time() + w_ext = cube(v) + dt0 = time() - t0 + + # (2) compute the result using NumPy, note the relative inefficiency + t0 = time() + w_ref = v * v * v + dt1 = time() - t0 + + # (3) compare both the results, be careful with NumPy temporary arrays + try: + assert(np.allclose(w_ext, w_ref)) + #assert(np.allclose(w_ext[-1,-1,-1], w_ref[-1,-1,-1])) + except: + raise + else: + print("OK! t(C)=%f, t(NumPy)=%f" % (dt0, dt1)) + + +if __name__ == "__main__": + test_pybex_cube() +