Intel® oneAPI Threading Building Blocks
Ask questions and share information about adding parallelism to your applications when using this threading library.
This community is designed for sharing of public information. Please do not share Intel or third-party confidential information here.

Splitting Constructors Odd

Your splitting constructors idea is odd. One rarely writes functions that modify arguments, much less a constructor that modifies another instance of the class. This smacks of std::auto_ptr!

I think a better approach is a static member function, split(), that would better parallel the join() member function. Thus, for parallel_reduce(), you'd write:

struct Example
Example(/* appropriate parameters */);

join(Example const &);

operator ()(tbb::blocked_range const &);

static Example
split(Example &);

This makes split() a factory function, of sorts. It is still a function that modifies its argument, but at least it isn't a constructor and the name clearly parallels that of join().

I'm sure you're concerned about backward compatiblity. You should be able to use template metaprogramming to determine whether the object given to parallel_reduce() is of a type that provides a static member function named split() or a splitting constructor while deprecating the latter.

0 Kudos
3 Replies

We considered this alternative while designing the class, but settled on the spitting constructor for efficiency reasons. Witha split() factory function, you would need an additional copy constructor call.

The range objects also share the "splittable" concept. In that case, splitting occurs frequently and efficiency is critical. We used a splitting constructor to avoid theoverhead of an additional copy constructor call at each split.

We've tried hard to provide a user-friendingly interface while still providing efficient execution. While it can be argued that the splitting constructor is a bit odd, we believe you'll get used to it quickly and be happy with the efficiency.

We can always provide a second method to do this if users demand it after some use. At this point, we think it is important to have a single, efficient way to do this.

You're probably right that one gets used to the splitting constructors quickly, but I still prefer unsurprising interfaces. If metaprogramming permits discovering which interface was provided, then supporting both would be a good idea.

Did you actually examine the results with mainstream compilers? I don't know the complexities of the code, but compilers are free to elide the copy construction thus keeping the efficiency you wanted.

[I was out enjoying an 8 week sabbatical.]

Yes, we considered a variety of interfaces, and as the former lead developer of KAI C++ I knewabout copy elision and return-slot optimization in C++ compilers. The splitting constructor approach seems to be the simplest for users to write efficiently without knowing the subtle points of catering to compiler's whims about what it can and cannot handle with respect to copy elision and return-slot optimization.