Passing Arguments to Std::Async by Reference Fails

Passing arguments to std::async by reference fails

It's a deliberate design choice/trade-off.

First, it's not necessarily possible to find out whether the functionoid passed to async takes its arguments by reference or not. (If it's not a simple function but a function object, it could have an overloaded function call operator, for example.) So async cannot say, "Hey, let me just check what the target function wants, and I'll do the right thing."

So the design question is, does it take all arguments by reference if possible (i.e. if they're lvalues), or does it always make copies? Making copies is the safe choice here: a copy cannot become dangling, and a copy cannot exhibit race conditions (unless it's really weird). So that's the choice that was made: all arguments are copied by default.

But then, the mechanism is written so that it actually fails to then pass the arguments to a non-const lvalue reference parameter. That's another choice for safety: otherwise, the function that you would expect to modify your original lvalue instead modifies the copy, leading to bugs that are very hard to track down.

But what if you really, really want the non-const lvalue reference parameter? What if you promise to watch out for dangling references and race conditions? That's what std::ref is for. It's an explicit opt-in to the dangerous reference semantics. It's your way of saying, "I know what I'm doing here."

why cant I pass a reference as a function argument for std::async

std::async passes the arguments to the callable by value (doesn't make perfect forwarding), hence you got the error because your callable only accepts reference.

You can use std::ref() to pass your var by reference.

How to pass a function and its parameters to std::async, inside a member function

Use std::ref to pass vstr by reference.

Because split_files is member function you need to pass this on which this function will be called.

auto fut = std::async(std::launch::async, &splitter::split_files, this, std::ref(vstr));

Live demo

I hope you are aware that execute function is blocking, you don't have any profit by starting async task inside it.

Why std::async cannot be used with functions that receive a reference to an abstract class as a parameter?

The arguments needs to be stored, which means they are copied. And references can't be copied.

Therefore a reference wrapper was introduced, that can store references while also being able to be copied. You can use it with the helper function std::ref and std::cref:

std::future<int> res = std::async(&func, std::ref(ac));  // Pass ac by reference

Using std::async with a method receiving a cv::OutputArray in order to assign it doesn't work

The root cause is that passing arguments by reference (&) to a function to run via std::async is problematic. You can read about it here: Passing arguments to std::async by reference fails (in your case there is no a compilation error, but the link explains the issue in general).

And in your case you use cv::OutputArray which is defined in opencv as a refernce type:

typedef const _OutputArray& OutputArray;

I assume you wanted the reference semantics, since you expected your result object to be updated by myFunc.

The solution is to use std::ref.

But since the result object you pass is a cv::Mat, it preferable and more straightforward that myFunc will receive a cv::Mat&.

I also managed to produce a solution using a cv::OutputArray, but it requires an ugly cast (in addition to the std::ref). It works fine on MSVC, but I am not sure it is will be generally valid.

Below is the code demostrating these 2 options. I recomend to use the 1st approach if you can. You can call otherFunc(result); at the point where I print the dimensions of result after it is initialized.

#include <opencv2/core/core.hpp>
#include <future>
#include <iostream>

// Test using a cv::Mat &:
class MyClass1
{
void myFunc(cv::Mat & dst) const
{
cv::Mat result(4, 3, CV_8UC1);
result = 1; // ... initialize some values
dst = result; // instead of using cv::OutputArray::assign
}
public:
void test()
{
cv::Mat result;
std::cout << "MyClass1: before: " << result.cols << " x " << result.rows << std::endl;
auto resultFuture = std::async(&MyClass1::myFunc, this, std::ref(result));
resultFuture.wait();
// Here result will be properly set.
std::cout << "MyClass1: after: " << result.cols << " x " << result.rows << std::endl;
}
};

// Test using a cv::OutputArray:
class MyClass2
{
void myFunc(cv::OutputArray dst) const
{
cv::Mat result(4, 3, CV_8UC1);
result = 1; // ... initialize some values
dst.assign(result);
}
public:
void test()
{
cv::Mat result;
std::cout << "MyClass2: before: " << result.cols << " x " << result.rows << std::endl;
auto resultFuture = std::async(&MyClass2::myFunc, this, std::ref(static_cast<cv::OutputArray>(result)));
resultFuture.wait();
// Here result will be properly set.
std::cout << "MyClass2: after: " << result.cols << " x " << result.rows << std::endl;
}
};

int main()
{
// Test receiving a cv::Mat&:
MyClass1 m1;
m1.test();
// Test receiving a cv::OutputArray:
MyClass2 m2;
m2.test();
return 0;
}

Why this code of CPP async gives error however the function without async works well?

std::thread and std::async do not accept references implicitly, you have to wrap them in std::ref:

std::async(std::launch::async, writeToDB, DB, &z, std::ref(logFile));

Why is passing by const ref slower when using std::async

The short story

given a copy and move-constructible type T

V f(T);
V g(T const&);

T t;
auto v = std::async(f,t).get();
auto v = std::async(g,t).get();

the only relevant difference concerning the two async calls is that, in the first one, t's copy is destroyed as soon as f returns; in the second, t's copy may be destroyed as per effect of the get() call.
If the async calls happen in a loop with the future being get() later, the first will have constant memory on avarage (assuming a constant per-thread workload), the second a linearly growing memory at worst, resulting in more cache hits and worse allocation performance.

The long story

First of all, I can reproduce the observed slowdown (consistently ~2x in my system) on both gcc and clang; moreover, the same code with equivalent std::thread invocations does not manifest the same behavior, with the const& version turning out slightly faster as expected. Let's see why.

Firstly, the specification of async reads:

[futures.async] If launch::async is set in policy, calls INVOKE(DECAY_COPY(std::forward(f)), DECAY_COPY(std::forward(args))...) (23.14.3, 33.3.2.2) as if in a new thread of execution represented by a thread object with the calls to DECAY_COPY() being evaluated in the thread that called async[...]The thread object is stored in the shared state and affects the behavior of any asynchronous return objects that reference that state.

so, async will copy the arguments forwarding those copies to the callable, preserving rvalueness; in this regard, it's the same as of std::thread constructor, and there's no difference in the two OP versions, both copy the vector.

The difference is in the bold part: the thread object is part of the shared state and will not be freed until the latter gets released (eg. by a future::get() call).

Why is this important ? because the standard does not specify to who the decayed copies are bound, we only know that they must outlive the callable invocation, but we don't know if they will be destroyed immediately after the call or at thread exit or when the thread object is destroyed (along with the shared state).

In fact, it turns out that gcc and clang implementations store the decayed copies in the shared state of the resulting future.

Consequently, in the const& version the vector copy is stored in the shared state and destroyed at future::get: this results in the "Start threads" loop allocating a new vector at each step, with a linear growth of memory.

Conversely, in the by-value version the vector copy is moved in the callable argument and destroyed as soon as the callable returns; at future::get, a moved, empty vector will be destroyed. So, if the callable is fast enough to destroy the vector before a new one is created, the same vector will be allocated over and over and memory will stay almost constant. This will result in less cache hits and faster allocations, explaining the improved timings.

Why can't I use reference in std::future parameters

Like std::thread, std::asyc passes the parameters by value to the "function". If you have a function that takes a reference you need to wrap the variable you are passing to asyc with std::ref like

#include <future>
#include <iostream>
#include <string>

int main()
{
int foo = 0;
bool bar = false;
std::future<std::string> async_request = std::async(
std::launch::async,
[=, &foo](bool& is_pumping_request) -> std::string {
return "str";
},
std::ref(bar)
);
std::cout << async_request.get() << std::endl;
}

Live Example

If the function takes a const & then you need to use std::cref.

Why can't `std::async` pick the correct overload?

Overload resolution happens based on types of arguments specified at the call site. When you're calling std::async, you're not calling calc_something but passing a pointer to the function to std::async. There are no calc_something call arguments and no way to resolve which of the two overloads' address to pass to std::async.

The compiler cannot use the subsequent arguments of std::async to resolve the overload. From the compiler's perspective, all std::async arguments are unrelated and nothing implies they will be used to invoke calc_something. In fact, you can call std::async with arguments of types different from those calc_something accepts, and it will work because they will get converted when calc_something is invoked.

In order to resolve this ambiguity, you must explicitly cast the pointer to calc_something to the exact type of the function pointer, matching one of the overloads.

std::async((std::size_t (*)(std::size_t))calc_something, 5);


Related Topics



Leave a reply



Submit