1

My main goal is to combine std::vector with ompenMP to make some parallel computations. I want to go with Z boson's answer where each thread works on its own copy of vector and at the end, we std::copy from all private vectors to the global one. Consider this example:

#include<iostream>
#include <vector>
const int N = 10;

class foo {
public:
    foo(int i) : heavy(i), ptr(nullptr) { }
    foo() : heavy(0), ptr(nullptr) { } // needed by std::vector.resize()

    const int heavy;  // type is not assignable ...
    foo * const ptr;  // drop const to make it work
};


int main() {
    std::vector<foo> tree;
    tree.resize(N);

    for (int i = 0; i < N; i += 2) {
        std::vector<foo> vec_private;
        vec_private.emplace_back(i  );
        vec_private.emplace_back(i+1);
        
        std::copy(vec_private.begin(), vec_private.end(), tree.begin() + i);
    }

    for (auto& x : tree)
        std::cout << x.heavy << '\n';

    return 0;
}
  1. I have a good reason to keep those consts in foo class. Is there any walk-around to keep them and not get a compile-time error?

  2. Would it be possible to employ move semantics for better performance (and possibly to solve problem 1) ?

For all I know the elements of std::vector must be stored as a contiguous block of memory, so I'm not sure if move semantics applies here.

Maverick
  • 420
  • 5
  • 16
  • 2
    Why do you need to resize `tree` then overwrite the elements you added? You don't do anything with those default-constructed elements, so why bother with them? Inserting instead of overwriting should work, no? (Is the reason related to your parallel processing?) – JaMiT Nov 06 '21 at 14:33
  • As Yalkov pointed out, inserting (with e.g. `back_inserter`) works only in a sequential approach, but with multiple threads, we must know the size of the `tree` a priori. – Maverick Nov 06 '21 at 23:20
  • 1
    Ah... there is a `+i` in the copy line. That's easy to miss, making this a good example of why you should always explain what your code is intended to do. Don't rely on others correctly interpreting your code, especially when you know your code is not fully correct (which is every question here, since nobody asks about correct code). – JaMiT Nov 07 '21 at 01:38

1 Answers1

3

Instead of calling resize and then copy-assigning your elements, you can reserve and then copy-initialize them:

std::vector<foo> tree;
tree.reserve(N);

for (int i = 0; i < N; i += 2) {
    ...
    std::copy(vec_private.begin(), vec_private.end(), std::back_inserter(tree));
}

This won't work if the goal is to have each thread copy their own data into a pre-allocated memory, in parallel. In such a case your options are:

  • Remove the const from the member -- a rather pragmatic approach.

  • Use uninitialized memory instead of a vector:

      foo *tree = (foo*)::operator new(sizeof(foo)*N);
      for (int i = 0; i < N; i += 2) {
          ...
          // then each thread can do:
          std::uninitialized_copy(vec_private.begin(), vec_private.end(), tree + i);
      }
    

    With this approach you'll need to call the destructors (tree[i].~foo()) and deallocate (::operator delete(tree)) correctly. This will be tricky though, because you'll need to keep track which threads copy-initialized their elements and which didn't.

Yakov Galka
  • 70,775
  • 16
  • 139
  • 220