What are the relative advantages of extending NumPy in Cython vs Boost.Python?
Asked Answered
S

2

7

I need to speed up some algorithms working on NumPy arrays. They will use std::vector and some of the more advanced STL data structures.

I've narrowed my choices down to Cython (which now wraps most STL containers) and Boost.Python (which now has built-in support for NumPy).

I know from my experience as a programmer that sometimes it takes months of working with a framework to uncover its hidden issues (because they are rarely used as talking points by its disciples), so your help could potentially save me a lot of time.

What are the relative advantages and disadvantages of extending NumPy in Cython vs Boost.Python?

Symbolism answered 23/1, 2017 at 19:7 Comment(3)
If you want more answers then unaccept mine! Accepting it kind of says "this problem is solved" when it's clearly only a partial answer.Officialdom
I have no way of knowing if it's partial, given the nature of the question. Apologies if this was bad SO etiquette.Symbolism
It isn't really etiquette (you can choose to accept whatever you like on your own question) - I just think you'd have the best chance of getting further answers without an accepted answer.Officialdom
O
7

This is a very incomplete answer that only really covers a couple of small parts of it (I'll edit it if I think of anything more):


Boost doesn't look to implement operator[] specifically for numpy arrays. This means that operator[] will come from the base object class (that ndarray inherits), which will mean the call will go through the Python mechanisms to __getitem__ and so indexing will be slow (close to Python speed). If you want to do indexing at speed you'll have to do pointer arithmetic yourself:

// rough gist - untested:

// i,j,k are your indices

double* data = reinterpret_cast<double*>(array.get_data());
// in reality you'd check the dtype - the data may not be a double...

double data_element = array.strides(0)*i + array.strides(1)*j +array.strides(2)*k;

In contrast Cython has efficient indexing of numpy arrays built in automatically.


Cython isn't great at things like std::vector (although it isn't absolutely terrible - you can usually trick it into doing what you want). One notable limitation is that all cdefs have to go at the start of the function so C++ classes with be default constructed there, and then assigned to/manipulated later (which can be somewhat inefficient). For anything beyond simple uses you do not want to be manipulating C++ types in Cython (instead it's better to write the code in C++ then call it from Cython).

A second limitation is that it struggles with non-class templates. One common example is std::array, which is templated with a number. Depending on your planned code this may or may not be an issue.

Officialdom answered 23/1, 2017 at 21:23 Comment(5)
Many thanks! Boost.Python's choice here is very surprising to me. [] is unary in C++03, but they could define operator() instead.Symbolism
Operataor[] is supported (the ndarray class does inherit from object which does define it) - it just won't be very quick. Off the top of my head I don't know how they do multiple arguments though, but I suspect you can.Officialdom
You mention the possibility to just write C++ and call it from Cython. This seems like a very intuitive approach but fundamentally different from writing Cython code. Do you have experience on the pros and cons of this approach?Travers
@Travers Problem 1 is that you need to understand C++ well enough, which doesn't apply to everyone (I'd argue that anyone who doesn't know C++ shouldn't be messing around with C++ and Cython though). Problem 2 is you need to be able to separate your algorithm cleanly into Cython part and C++ part - i.e. you can't easily "just lookup a Python value" in your C++ part.Officialdom
@Officialdom Thank you very much. In this case, this seems to actually be the favorable solution for me, at least if I just have small performance relevant functions. Rather, improve my C++ knowledge than learn how to optimize Cython.Travers
B
4

For small one shot problems, I tend to prefer cython, for larger integration with c++ code bases, prefer boost Python.

In part, it depends on the audience for your code. If you're working with a team with significant experience in python, but little experience of using C++, Cython makes sense. If you have a fixed code base with complex types to inter operate with, the boost python can end up being a little cheaper to get running.

Cython encourages you to write incrementally, gradually adding types as required to get extra performance and solves many of the hard packaging problems. boost Python requires a substantial effort in getting a build setup, and it can be hard to produce packages that make sense on PyPI

Cython has good built in error messages/diagnostics, but from what I've seen, the errors that come out of boost can be very hard to interpret - be kind to yourself and use a new-ish c++ compiler, preferably one known for producing readable error messages.

Don't discount alternative tools like numba (similar performance to cython with code that is Python, not just something that looks similar) and pybind11 (boost Python without boost and with better error messages)

Brawny answered 23/1, 2017 at 20:57 Comment(2)
Many thanks! I had a horrendous time trying to pip install numba : unstated dependencies and, even if you fix them, broken builds, same for their sub-dependencies, and so on recursively. Even if they didn't do this intentionally, to make people buy Conda, it turned me off to their project.Symbolism
I would also recommend giving theano a change. It is only usable for a certain subset of (purely mathematical) problem. But in this subset it works great. I already tried numba, pythran and others. theano was the only one which I could get to run reliably and without too much fuss.Travers

© 2022 - 2024 — McMap. All rights reserved.