C ++ Boost Bind Performance
Asked Answered
V

2

6

Are there any performance impacts (positive or negative) when binding functions (using Boost Bind) ?

Virgenvirgie answered 16/9, 2012 at 11:45 Comment(0)
P
11

Maybe, may not be. It depends.

The result of std::bind (or also boost::bind) is a so-called "bind expression", which has an un­know­able type determined by the implementation. This type is a Callable, and it is convertible to an in­stance of std::function (or boost::function).

Internally, function (may) use type erasure to handle various complex, stateful "call­able objects". This entails a dynamic allocation and a virtual dispatch in some (though not neces­sari­ly all) cases. Both bind and function are stateful, since they store the bound arguments.

The upshot is that you should avoid converting a bind expression to a function object if possible. The bind expression itself may be cheaper, and you should not be afraid of using bind (for example when bind­ing member function pointers to instances and arguments). Use bind freely, but conversion to function only if you truly need to manage a heterogeneous collection of callable entities.

Here are two typical examples:

Bad; avoid this:

std::function<int(bool, char)> f = std::bind(&Foo::bar, x, 12);

void do_something(std::function<int()> func, int & acc)
{
    acc += func();
}

Better; prefer this:

auto f = std::bind(&Foo::bar, x, 12);   // unknowable type, but perfectly fine

template <typename F>
void do_something(F && func, int & acc)  // can deduce unknowable types
{
    acc += func();
}
Periotic answered 16/9, 2012 at 12:11 Comment(3)
More accurately, std::function is constructible from that. After all, there's no problem with std::function<...>(boost::bind(...)). In addition, your second example should really either take func by value or by rvalue reference, because it is now impossible to have do_something(bind(...), acc)Palazzo
I disagree with your second paragraph, you know the bind expression is Callable. Among other things that implies it can be converted to std::function like any other callable type, but you have the focus wrong to say all you know about the bind expr is it converts to std::function. I don't even know why you've mentioned std::function, it's not relevant to the question. std::bind has no need to use type erasure or dynamic dispatch.Goofball
@JonathanWakely: Hm, you're right... I suppose because the question was somewhat open-ended, I wanted to address use cases for bind and point out some pros and cons. Please do feel free to make changes.Periotic
G
9

boost::bind and std::bind will copy their arguments so that the returned object contains a copy of each argument, including the function object. If those arguments are expensive to copy then it will be expensive to pass them to std::bind.

You can think about it similarly to creating a tuple of all the arguments, e.g.

auto b = std::bind(func, arg1, arg2, arg3);

should be about equivalent in performance to:

auto b = std::make_tuple(func, arg1, arg2, arg3);

If you don't want the arguments to be copied, use the ref utility to pass them in a reference_wrapper which is a very lightweight type that stores a pointer to the object:

auto b = std::bind(func, std::ref(arg1), arg2, arg3);

When calling the bound function, each of the bound arguments will be passed to the bound function, as lvalues (i.e. no perfect forwarding):

b();  // equiv to std::get<0>(b)(std::get<1>(b), std::get<2>(b), std::get<3>(b))

If the function takes its arguments by value, then the bound arguments will be copied into the function arguments. That could be expensive, but it's exactly the same whether you call the function directly or call it inside the result of std::bind ... it's a property of the function being called, not the bind expression.

So the only overhead of using boost::bind is in the initial copying of the bound arguments, which you can control either by moving the arguments in to avoid copying:

auto b = std::bind(func, std::move(arg1), arg2, arg3);

or passing them by reference:

auto b = std::bind(func, std::ref(arg1), arg2, arg3);

The above discussion ignores the features of bind such as placeholders and calling nested bind expressions, but those do not affect performance and everything above still applies.

Goofball answered 14/1, 2013 at 1:25 Comment(2)
I suppose the true lightweight wrapper for passing-by-value is std::cref, and std::ref should be reserved for those situations where you actually want a modifiable reference?Periotic
I think tend to use ref by default. If I want to call f(const X&) with a non-const lvalue x I don't bother to say f(const_cast<const X&>(x)) because the right thing will happen anyway, similarly for ref and cref. But cref does have the advantage of being only one more character to type, so maybe I should default to using it, just as I default to using cbegin() and cend() if I don't want modifable iterators.Goofball

© 2022 - 2024 — McMap. All rights reserved.