Converting concurrent_vector to std::vector
Asked Answered
M

2

6

I'm looking for the recommended way to convert a concurrent_vector from the PPL library to a normal std::vector.

I have a function which returns its results in a std::vector but may or may not use parallel algorithms internally. Currently I'm using insert to copy the elements from the concurrent vector to the normal vector:

#ifdef PARALLEL
    std::vector<Foo> Bar() {
        concurrency::concurrent_vector<Foo> cv;

        //Fill cv in a parallel algorithm

        std::vector<Foo> sv;
        sv.insert(sv.begin(), cv.begin(), cv.end());
        return sv;
    }
#else
    std::vector<Foo> Bar() {
        std::vector<Foo> sv;

        //Fill sv in a sequential algorithm

        return sv;
    }
#endif

Although the performance of the insert operation is not a real issu at the moment (compared to the function body), it just seems unnecessary and I wonder whether there is a better solution (Btw.: Foo is a simple POD which cannot be moved).

Ideally I would like to have something similar to this

std::vector<Foo> Bar() {
    concurrency::concurrent_vector<Foo> cv;

    //Fill cv in a parallel algorithm

    return static_cast<std::vector<Foo>>(cv);
}

or at least

std::vector<Foo> Bar() {
    concurrency::concurrent_vector<Foo> cv;

    //Fill cv in a parallel algorithm

    std::vector<Foo> sv(std::move(cv));
    return sv;
}

Are there any suggestions on how to do this properly?


EDIT:
As always I overlooked the most obvious simplification (suggested by Chris):

std::vector<Foo> Bar() {
    concurrency::concurrent_vector<Foo> cv;

    //Fill cv in a parallel algorithm

    return std::vector<Foo>(cv.begin(), cv.end());
}

while it (most probably) doesn't get rid of the copies it looks much cleaner.

EDIT2:

This leads me to the qestion of - assuming there is no way to explicitly prevent a copy of the vector data - how likely it is that the compiler can optimize the copy (from concurrent to std::vector) away, while still applying RVO or a move operation on the functions' return value

Molybdenum answered 5/6, 2014 at 17:14 Comment(5)
No need for insert. Use the constructor.Dissemble
You can save a few lines by using return {cv.begin(), cv.end()}; but I don't think there's any way other than copying.Valerievalerio
I'd probably use return {std::make_move_iterator(cv.begin()), std::make_move_iterator(cv.end())};, although it obviously makes no difference if move is equivalent to copy for Foo.Panatella
@Chris: Good idea. While this doesn't prevent the copy, the code would look much cleaner that wayMolybdenum
@Panatella I was just typing this up :) As you said, definitely the better option if Foo is moveable.Valerievalerio
M
4

assuming there is no way to explicitly prevent a copy of the vector data - how likely it is that the compiler can optimize the copy (from concurrent to std::vector) away, while still applying RVO or a move operation on the functions' return value?

concurrent_vector is quite different from std::vector. In particular, its data storage is not contiguous in memory, as std::vector requires. Thus the conversion you seek for is impossible, whether explicit or as a compiler optimization; data need to be copied from one location to another.

Miyokomizar answered 14/8, 2014 at 12:22 Comment(1)
Thanks, I have to admit that I overlooked that "detail".Molybdenum
F
0

Another option is to always use std::vector with parallel algorithms.

Algorithms and containers are normally independent / orthogonal, so you can use, for example the standard algorithms in Parallel Mode with g++, same applies to TBB algorithms.

Flout answered 6/6, 2014 at 9:8 Comment(2)
Thanks for the suggestion, but unless I missunderstand something, I believe this doesn't apply to my situation. The "parallel algorithm" is a parallel_for_each, where - depending on the the result of the loop body - items may or may not be appended to the vector. Of course I could guard the vector access manually with a mutex, but I believe the usage of a thread safe container makes the code less error prone, so I would like to stick to concurrent_vector. This has also the advantage, that the loop bodies of the parallel and sequential version can be absolute identical.Molybdenum
@Molybdenum I see. In this case you could append the results to different vectors (one for each thread) and merge the vectors at the end in the calling thread. That could be even faster because the worker threads would not contend on appending to the same vector, however, your loop would probably have to be different for parallel and non-parallel versions.Flout

© 2022 - 2024 — McMap. All rights reserved.