dimanche 29 janvier 2017

Should I pass allocator as a function parameter? (my misunderstanding about allocator)

After I am studying about allocator for a few days by reading some articles
(cppreference and Are we out of memory) ,
I am confused about how to control a data-structure to allocate memory in a certain way.

I am quite sure I misunderstand something,
so I will divide the rest of question into many parts to make my mistake easier to be refered.

Here is what I understand so far ...

Snippet

Suppose that fb() is an existing function that generate a list of B from a list of BPrototype.
fb() is used in B() constructor:-

class B{
    public: std::vector<B> fb(){
        std::vector<BPrototype> prototypes = getPrototypes();
        std::vector<B> result;                     //#X
        for(int n=0;n<prototypes.size();n++){
            //construct real object  (BPrototype->B)
            result.push_back(makeItBorn(prototypes[n])); 
        }
        return result;
    }
    std::vector<B> bField;    //#Y
    public: B(){
        this->bField=fb();    //#Y  ; "fb()" is called only here
    }
    //.... other function, e.g. "makeItBorn()" and "getPrototypes()"
};

From the above code, std::vector<B> currently uses a generic default std::allocator.

For simplicity, from now on, let's say there are only 2 allocators (beside the std::allocator) ,
which I may code it myself or modify from somewhere :-

  • HeapAllocator
  • StackAllocator

Part 1 (#X)

This snippet can be improved using a specific type allocator.
It can be improved in 2 locations. (#X and #Y)

std::vector<B> at line #X seems to be a stack variable,
so I should use stack allocator :-

std::vector<B,StackAllocator> result;   //#X

This tends to yield a performance gain. (#X is finished.)

Part 2 (#Y)

Next, the harder part is in B() constructor. (#Y)
It would be nice if the variable bField has an appropriate allocation protocol.

Just coding the caller to use allocator explicitly can't achieve it, because the caller of constructor can only do as best as :-

std::allocator<B> bAllo;   
B* b = bAllo.allocate(1);   

which does not have any impact on allocation protocol of bField.

Thus, it is duty of constructor itself to pick a correct allocation protocol.

Part 3

I can't know whether an instance of B will be constructed as a heap variable or a stack variable.
It is matter because this information is importance for picking a correct allocator/protocol.

If I know which one it is (heap or stack), I can change declaration of bField to be:-

std::vector<B,StackAllocator> bField;     //.... or ....
std::vector<B,HeapAllocator> bField;     

Unfortunately, with the limited information (I don't know which it will be heap/stack, it can be both),
this path (using std::vector) leads to the dead end.

Part 4

Therefore, the better way is passing allocator into constructor:-

MyVector<B> bField; //create my own "MyVector" that act almost like "std::vector"
public: B(Allocator* allo){
    this->bField.setAllocationProtocol(allo);  //<-- run-time flexibility 
    this->bField=fb();   
}

It is tedious because callers have to pass an allocator as an additional parameter,
but there are no other ways.

Question

  • Where do I start to go wrong, how?
  • How can I improve the snippet to use appropriate allocator (#X and #Y)?
  • When should I pass allocator as a parameter?

It is hard to find a practical example about using allocator.

Aucun commentaire:

Enregistrer un commentaire