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 (mis)understand :-
Snippet
Suppose that B::generateCs()
is a function that generates a list of C
from a list of CPrototype
.
The B::generateCs()
is used in B()
constructor:-
class C {/*some trivial code*/};
class CPrototype {/*some trivial code*/};
class B {
public:
std::vector<C> generateCs() {
std::vector<CPrototype> prototypes = getPrototypes();
std::vector<C> result; //#X
for(std::size_t n=0; n < prototypes.size(); n++) {
//construct real object (CPrototype->C)
result.push_back( makeItBorn(prototypes[n]) );
}
return result;
}
std::vector<C> bField; //#Y
B() {
this->bField = generateCs(); //#Y ; "generateCs()" is called only here
}
//.... other function, e.g. "makeItBorn()" and "getPrototypes()"
};
From the above code, std::vector<C>
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<C>
at line #X
seems to be a stack variable,
so I should use stack allocator
:-
std::vector<C,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<C,StackAllocator> bField; //.... or ....
std::vector<C,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<C> bField; //create my own "MyVector" that act almost like "std::vector"
B(Allocator* allo) {
this->bField.setAllocationProtocol(allo); //<-- run-time flexibility
this->bField = generateCs();
}
It is tedious because callers have to pass an allocator as an additional parameter,
but there are no other ways.
Moreover, it is the only practical way to gain the below data-coherence advantage when there are many callers, each one use its own memory chunk:-
class System1 {
Allocator* heapForSystem1;
void test(){
B b=B(heapForSystem1);
}
};
class System2 {
Allocator* heapForSystem2;
void test(){
B b=B(heapForSystem2);
}
};
Question
- Where did 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.
Edit (reply Walter)
... using another than std:allocator<> is only rarely recommendable.
For me, it is the core of Walter's answer.
It would be a valuable knowledge if it is reliable.
1. Are there any book/link/reference/evidence that support it?
The list doesn't support the claim. (It actually supports the opposite a little.)
Is it from personal experience?
2. The answer somehow contradict with many sources. Please defense.
There are many sources that recommend not to use std:allocator<>
.
- Are we out of memory :
Can't answer "How much memory are you using for subsystem X?" is a guilty. - Custom C++ allocators suitable for video games
It implies that custom allocator is a must for console games.
@ section "Why replace the default allocator?" - Memory Management part 1 of 3
without custom allocator = "every now and then there’s a little lag (in game)"
More specifically, are they just a "hype" that rarely worth using in real world?
Another small question :-
Can the claim be expanded to "Most quality games rarely use custom allocator"?
3. If I am in such rare situation, I have to pay the cost, right?
There are only 2 good ways:-
- passing allocator as template argument, or
- as a function's (including constructor) parameter
- (another bad approach is to create some global flag about what protocol to use)
Is it correct?