Reputation: 8617
I am using the imperative form of tbb::parallel_reduce(range,body)
as documented here. The doc says:
A parallel_reduce uses the splitting constructor to make one or more copies of the body for each thread. It may copy a body while the body’s operator()or method join runs concurrently. You are responsible for ensuring the safety of such concurrency.
So I understand that Body::Body( Body&, split )
can run as Body::operator()
or Body::join( Body& rhs )
do.
My questions are:
1) Can Body::operator()
and Body::join( Body& rhs )
be run concurrently?
2) Can Body::join( Body& rhs )
safely modify rhs
?
W.r.t. 1), I believe join
can be called after operator()
is done only. Also, looking at the example in Intel's doc, it seems clear they can modify the same data without data race issues:
struct Sum {
float value;
...
void operator()( const blocked_range<float*>& r ) {
value = ...;
}
void join( Sum& rhs ) {value += rhs.value;}
};
Upvotes: 0
Views: 621
Reputation: 12784
The lifetime of a Body
object used by tbb::parallel_reduce
is:
operator()
is applied to one or more consequent ranges.join()
is called for each object split from it in #3.rhs
back to the body object it was split from.Of the above operations, #3 can run concurrently to #2 and #4; all the rest is sequential, even though not necessarily called by a single thread. In other words, while a body accumulates partial reduction for some subrange(s) or joins the result from another body, it can be updated by splitting constructors of new bodies. This is where protection might be necessary, but typically these calls access different parts of the body.
So, the answers to your questions are:
1) Can
Body::operator()
andBody::join( Body& rhs )
be run concurrently?
No. join()
is only called after all calls to operator()
, both for this
and rhs
.
2) Can
Body::join( Body& rhs )
safely modifyrhs
?
Yes, it can. But except possibly for moving its content during the join and updating the state accordingly, modifying rhs
makes little sense because it will be destroyed right after the join()
call.
Upvotes: 2
Reputation: 6537
Documentation is not clear enough on what the instances are involved in concurrent call to the methods. But it hints:
In typical usage, the safety requires no extra effort.
There is no concurrency for the same instance. So, it is safe to split or join both instances as in a serial code. operator()
cannot be called concurrently on the same instance either. So that the documentation must really say:
It may copy a body while the body’s operator() or method join runs concurrently on different instances.
BTW, the quote
A parallel_reduce recursively splits the range into subranges to the point such that is_divisible() is false for each subrange.`
is true for simple_partitioner
only, for other partitioners, it can stop splitting before reaching is_divisible() == false
Upvotes: 1