Skip to content
Snippets Groups Projects
Commit 5759530a authored by Repo Updater's avatar Repo Updater
Browse files

b10c604c polish pybind11 example, add README.md to f2py example

parent a95c8dbf
No related branches found
No related tags found
No related merge requests found
Showing with 95 additions and 26 deletions
# directories # directories
__pycache__ #__pycache__
.unison .unison
.ipynb_checkpoints .ipynb_checkpoints
# files # files
*.so #*.so
*.o #*.o
*.lprof *.lprof
*.tmp *.tmp
...@@ -2,11 +2,10 @@ ...@@ -2,11 +2,10 @@
## Features ## 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 * `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` 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 ## Usage
...@@ -15,5 +14,5 @@ ...@@ -15,5 +14,5 @@
`python -c "from foobar import hello; hello.say_hello()"` `python -c "from foobar import hello; hello.say_hello()"`
* or install the package, and run the extension from anywhere * or install the package, and run the extension from anywhere
`python setup.py install --user` `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
...@@ -2,7 +2,8 @@ import os ...@@ -2,7 +2,8 @@ import os
from setuptools import setup, Extension from setuptools import setup, Extension
ext = Extension("foobar.hello", ext = Extension("foobar.hello",
sources=["foobar/hello.pyx", "foobar/c_hello.c"]) sources=["foobar/hello.pyx", "foobar/c_hello.c"]
)
setup(name="foobar", setup(name="foobar",
ext_modules=[ext], ext_modules=[ext],
......
...@@ -6,10 +6,10 @@ ...@@ -6,10 +6,10 @@
* `hello.pyx` contains the Python interface written in Cython * `hello.pyx` contains the Python interface written in Cython
* `c_hello.c` contains a hello-world implementation in C, however, in contrast * `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, 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) in the directory `libhello`.
shared object. Use `make` to generate the shared object in that directory.
* `setup.py` contains code to compile the extension, demonstrating how to link * `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 ## Usage
......
all: libhello.so all: libhello.so
libhello.so: c_hello.c libhello.so: c_hello.c
gcc -Wall -fPIC -c c_hello.c $(CC) -Wall -fPIC -c c_hello.c
gcc -shared -o $@ c_hello.o $(CC) -shared -o $@ c_hello.o
.PHONY: clean .PHONY: clean
clean: clean:
......
import os import os
from setuptools import setup, Extension from setuptools import setup, Extension
libhello_dir = os.path.abspath("./libhello") libhello_dir = os.path.abspath("./libhello")
# We need to specify the location of the include file. # We need to specify the location of the include file.
...@@ -19,7 +18,8 @@ ld_flags.append("-lhello") # link libhello ...@@ -19,7 +18,8 @@ ld_flags.append("-lhello") # link libhello
ext = Extension("wrap_libhello.hello", ext = Extension("wrap_libhello.hello",
sources=["wrap_libhello/hello.pyx"], sources=["wrap_libhello/hello.pyx"],
extra_link_args=ld_flags, extra_link_args=ld_flags,
include_dirs=include_dirs) include_dirs=include_dirs
)
setup(name="wrap_libhello", setup(name="wrap_libhello",
ext_modules=[ext], ext_modules=[ext],
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
## Features ## 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 * `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 * `cube.pyx` contains the Python interface written in Cython
* `setup.py` contains code to compile the extension with optimizing compiler flags * `setup.py` contains code to compile the extension with optimizing compiler flags
......
...@@ -16,14 +16,15 @@ c_flags.append("-fopt-info") ...@@ -16,14 +16,15 @@ c_flags.append("-fopt-info")
include_dirs = [] include_dirs = []
include_dirs.append(np.get_include()) include_dirs.append(np.get_include())
# extra link flags are possible as well; we leave them empty here # extra link flags are possible as well; here we use the same as for compilation to link to libgomp
ld_flags = [] ld_flags = c_flags
ext = Extension("ctonumpy.cube", ext = Extension("ctonumpy.cube",
sources=["ctonumpy/cube.pyx", "ctonumpy/c_cube.c"], sources=["ctonumpy/cube.pyx", "ctonumpy/c_cube.c"],
extra_compile_args=c_flags, extra_compile_args=c_flags,
extra_link_args=ld_flags, extra_link_args=ld_flags,
include_dirs=[np.get_include()]) include_dirs=[np.get_include()]
)
setup(name="ctonumpy", setup(name="ctonumpy",
ext_modules=[ext], ext_modules=[ext],
......
# 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'.
File moved
# 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
...@@ -2,10 +2,15 @@ from setuptools import setup ...@@ -2,10 +2,15 @@ from setuptools import setup
# run `pip install --user pybind11` if not yet installed # run `pip install --user pybind11` if not yet installed
from pybind11.setup_helpers import Pybind11Extension, build_ext 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 = [ ext_modules = [
Pybind11Extension( Pybind11Extension(
"pybex", "pybex",
["src/pybex.cpp",], ["src/pybex.cpp",],
extra_compile_args=extra_gcc_args,
extra_link_args=extra_gcc_args
), ),
] ]
......
// pybind11 example module 'pybex' // pybind11 example module 'pybex'
#include <vector>
#include <numeric>
#include <functional>
#include <pybind11/pybind11.h> #include <pybind11/pybind11.h>
#include <pybind11/numpy.h> #include <pybind11/numpy.h>
namespace py = pybind11;
// C/C++ implementation of the function to be wrapped // C/C++ implementation of the function to be wrapped
void c_cube(const double * v_in, double * v_out, size_t n_elem) { 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) { for (size_t i=0; i<n_elem; ++i) {
v_out[i] = v_in[i] * v_in[i] * v_in[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 // 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 // obtain information about the input array
auto v_out = py::array_t<double>(v_in.size()); py::buffer_info pybuf_in = np_in.request();
double * v_out_ptr = (double*) v_out.request().ptr; 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) { PYBIND11_MODULE(pybex, m) {
m.doc() = "pybind11 example module"; // module docstring m.doc() = "pybind11 example module"; // module docstring
......
#!/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()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment