c++stlallocationxeon-phicilk-plus

is it ok to construct from a stl container which has a different allocator?


Say I have

std::vector<T, allocator1=default allocator in STL>  A

std::vector<T, allocator2=some other allocator> B

I am using __offload::shared_allocator as allocator2 in Cilk Plus for offloading from Host CPU to Xeon Phi coprocessor

Can I construct A from B?

std::vector<T> A{B.begin(), B.end()};

And more general, for which kind of STL functions that different allocators will matter?


Solution

  • As noted in the comments, the constructor

    std::vector<T> A{B.begin(), B.end()};
    

    should work fine since it works for any two iterators. As for the question "for which kind of STL functions that different allocators will matter?", there's two kinds of "different":

    1. Allocators with different types.
    2. Allocators with the same type, but compare as different.

    For incompatibilities with respect to (1), you should see a compile-time error because the type system will catch it. For (2), I don't know if STL implementations catch the possible problems or not. The primary container/function to worry about in that regard are the splice methods on class list (and splice_after methods on class forward_list), because they move an object allocated in one container to another container. As footnote 265 of the C++11 standard notes, the STL containers require that allocators compare equal, so such movement should not be a problem.

    With respect to offload issues, if A is allocated with a plain STL allocator, then it cannot be constructed on the host side and used on the coprocessor side. But B can be used on both sides. Here is an example that constructs B using anoffload::shared_allocator, and constructsA` from it on the coprocessor side.

    #pragma offload_attribute (push, _Cilk_shared)
    #include <vector>
    #include "offload.h"
    #include <cstdio>
    #pragma offload_attribute (pop)
    
    _Cilk_shared std::vector<int, __offload::shared_allocator<int> > B;
    
    _Cilk_shared void foo() {
    #ifdef __MIC__
        std::vector<int> A(B.begin(),B.end());
        for( auto& x: A )
            std::printf("%d\n", x);
    #else
        std::printf("Host\n");
    #endif
    }
    
    int main() {
        auto y = 1;
        for( int i=0; i<10; ++i ) {
            B.push_back(y);
            y *= 10;
        }
        _Cilk_offload foo();
    }
    

    It should print:

    1
    10
    100
    1000
    10000
    100000
    1000000
    10000000
    100000000
    1000000000