How to use allocators in modern C++ - c++

From what I read in http://en.cppreference.com/w/cpp/memory/allocator , most features of the allocators are now going to be deprecated. The question is, how is one supposed to use allocators in new code? What is the "right" way now?
From what I deduce in the documentation, construct is part of the allocator traits, rather than the allocator itself.
I am building a custom container, here it is a very simple version of the constructor, is this a good usage of the new design?
container::container(std::size_t size, T const& value, Allocator const& allocator) : allocator_(allocator){
data_ = std::allocator_traits<Alloc>::allocate(allocator_, size);
for(auto ptr = data_; ptr != data_ + size; ++ptr){
std::allocator_traits<Allocator>::construct(allocator_, ptr, value)
}
}
I tried to use an algorithm (like std::for_each) in the loop but I didn't manage to use one without taking addresses (operator&).
Where can I find a complete example of a modern allocator?
After some tweaking, I found a way to use an algorithm instead of the the raw loop (to which an execution policy can be passed). I am not very sure, but it could be this:
data_ = std::allocator_traits<Allocator>::allocate(allocator_, size);
std::for_each([policy? deduced from allocator?,]
boost::make_counting_iterator(data_),
boost::make_counting_iterator(data_ + size),
[&](auto ptr){std::allocator_traits<Allocator>::construct(allocator_, ptr, value);}
);

Yes, the current approach is through std::allocator_traits. You'll be able to support the "minimal allocator interface" that way.
http://en.cppreference.com/w/cpp/concept/Allocator
Some requirements are optional: the template std::allocator_traits supplies the default implementations for all optional requirements, and all standard library containers and other allocator-aware classes access the allocator through std::allocator_traits, not directly.
If you observe the std::allocator_traits member functions and typedefs, you'll see they're detecting the presence of appropriate function/types and dispatching through them if they can.
The deprecation and potential future removal will change nothing if you're already using std::allocator_traits since it only applies to std::allocator and their member functions/typedefs.
Now, if you ask me, there's nothing wrong with for-loops, and using std::for_each gains you nothing. There are several uninitialized_* functions, but they use placement new directly. If you really care you can extract this code to a separate construct_range function.
There's also an exception safety issue - in case one of the constructors throws, you need to destroy the earlier elements in order to satisfy the strong exception guarantee and free the memory too (destructor won't get called in case constructor throws)

Related

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 (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?
In C++, the allocator used for the standard containers is tied to the container type (but see below). Thus, if you want to control the allocation behaviour of your class (including its container members), the allocator must be part of the type, i.e. you must pass it as a template parameter:
template<template <typename T> Allocator>
class B
{
public:
using allocator = Allocator<C>
using fieldcontainer = std::vector<C,allocator>;
B(allocator alloc=allocator{})
: bFields(create_fields(alloc)) {}
private:
const fieldcontainer bFields;
static fieldcontainer create_fields(allocator);
};
Note, however, that there is experimental polymorphic allocator support, which allows you change the allocator behaviour independently of the type. This is certainly preferable to designing your own MyVector<> template.
Note that using another than std::allocator<> is only recommendable if there is a good reason. Possible cases are as follows.
A stack allocator may be preferred for small objects that are frequently allocated and de-allocated, but even the heap allocator may not be less efficient.
An allocator that provides memory aligned to, say, 64bytes (suitable for aligned loading into AVX registers).
A cache-aligned allocator is useful to avoid false sharing in multi-threaded situations.
An allocator could avoid default initialising trivially constructible objects to enhance performance in multi-threaded settings.
note added in response to additional questions.
The article Are we out of memory dates from 2008 and doesn't apply to contemporary C++ practice (using the C++11 standard or later), when memory management using std containers and smart pointers (std::unique_ptr and std::shared_ptr) avoids memory leaks, which are the main source of increasing memory demand in poorly written code.
When writing code for certain specific applications, there may well be good reasons to use a custom allocator -- and the C++ standard library supports this, so this is a legitimate and appropriate approach. The good reasons include those listed already above, in particular when high performance is required in a multi-threaded environment or to be achieved via SIMD instructions.
If memory is very limited (as it may be on some game consoles), a custom allocator cannot really magically increase the amount of memory. So in this case the usage of the allocator, not the allocator itself, is most critical. A custom allocator may help reducing memory fragmentation, though.
It sounds like you are misunderstanding what a stack allocator is. A stack allocator is just an allocator that uses a stack, the data structure. A stack allocator can manage memory that is either allocated on the stack or the heap. It is dangerous to use if you don't know what you are doing as a stack allocator deallocates all the memory past the specified pointer when deallocate is called. You can use a stack allocator for when the most recently initialized element in a data structure is always the next one destroyed (or if you end up destroying them all at once in the end).
You can look at some of the std collections to see how they allow programmers to supply a specified allocator such as std::vector. They use an optional template argument so the user can choose the allocator class. It also allows you to pass the allocator in as an instance if you want to. If you don't, it instantiates one with the default constructor. If you don't choose an allocator class, then it uses the default allocater which just uses the heap. You could do the same.
template<typename C, typename Allocator = std::allocator<C> >
class B {
vector<C, Allocator> bField;
void generateCs() {
std::vector<CPrototype> prototypes = getPrototypes();
for(std::size_t n=0; n < prototypes.size(); n++) {
//construct real object (CPrototype->C)
bField.push_back( makeItBorn(prototypes[n]) );
}
}
B(const Allocator& allo = Allocator()) : bField(allo) {
generateCs();
}
}
This allows the user to have control over allocation when they want to, but they also ignore it if they don't care

Multi-type allocator design

I'm working on the runtime library for a dynamic language, and more specifically on its memory allocation.
I will use garbage collection, but I thought it would be nice to allow the user to use its own memory allocator if it needs to. However, after looking at the standard c++ allocator interface, I didn't seen any way to have a generic allocator (except by templating on char, but it seems hacky).
Is it a good idea to use the standard allocator interface ?
If it is, what would be a possible design for allocating multiple types ?
Note : My library use C++11, so I'm talking about the 'new' allocator interface.
As #Cubbi said, look at allocator::rebind. The actual type argument to an allocator is pretty-much irrelevant. Standard containers use allocator::rebind to change to the type that they actually need. In particular, associative containers need to allocate nodes that hold their own data as well as the data objects that the container nominally holds. So allocator::rebind is used to get an allocator<Node<T>> from an allocator<T>.
Depends on whether you want compile-time or run-time polymorphism. The standard allocator is good for the former case. For the latter, you can do something like:
class Allocator {
public:
virtual void* allocate(std::size_t size) = 0;
virtual void deallocate(void* p) = 0;
}
The users would be able to customize the allocator via the virtual functions.

Does std::vector *have* to move objects when growing capacity? Or, can allocators "reallocate"?

A different question inspired the following thought:
Does std::vector<T> have to move all the elements when it increases its capacity?
As far as I understand, the standard behaviour is for the underlying allocator to request an entire chunk of the new size, then move all the old elements over, then destroy the old elements and then deallocate the old memory.
This behaviour appears to be the only possible correct solution given the standard allocator interface. But I was wondering, would it make sense to amend the allocator to offer a reallocate(std::size_t) function which would return a pair<pointer, bool> and could map to the underlying realloc()? The advantage of this would be that in the event that the OS can actually just extend the allocated memory, then no moving would have to happen at all. The boolean would indicate whether the memory has moved.
(std::realloc() is maybe not the best choice, because we don't need do copy data if we cannot extend. So in fact we'd rather want something like extend_or_malloc_new(). Edit: Perhaps a is_pod-trait-based specialization would allow us to use the actual realloc, including its bitwise copy. Just not in general.)
It seems like a missed opportunity. Worst case, you could always implement reallocate(size_t n) as return make_pair(allocate(n), true);, so there wouldn't be any penalty.
Is there any problem that makes this feature inappropriate or undesirable for C++?
Perhaps the only container that could take advantage of this is std::vector, but then again that's a fairly useful container.
Update: A little example to clarify. Current resize():
pointer p = alloc.allocate(new_size);
for (size_t i = 0; i != old_size; ++i)
{
alloc.construct(p + i, T(std::move(buf[i])))
alloc.destroy(buf[i]);
}
for (size_t i = old_size; i < new_size; ++i)
{
alloc.construct(p + i, T());
}
alloc.deallocate(buf);
buf = p;
New implementation:
pair<pointer, bool> pp = alloc.reallocate(buf, new_size);
if (pp.second) { /* as before */ }
else { /* only construct new elements */ }
When std::vector<T> runs out of capacity it has to allocate a new block. You have correctly covered the reasons.
IMO it would make sense to augment the allocator interface. Two of us tried to for C++11 and we were unable to gain support for it: [1] [2]
I became convinced that in order to make this work, an additional C-level API would be needed. I failed in gaining support for that as well: [3]
In most cases, realloc will not extend the memory, but rather allocate a separate block and move the contents. That was considered when defining C++ in the first place, and it was decided that the current interface is simpler and not less efficient in the common case.
In real life, there are actually few cases where reallocis able to grow. In any implementation where malloc has different pool sizes, chances are that the new size (remember that vector sizes must grow geometrically) will fall in a different pool. Even in the case of large chunks that are not allocated from any memory pool, it will only be able to grow if the virtual addresses of the larger size are free.
Note that while realloc can sometimes grow the memory without moving, but by the time realloc completes it might have already moved (bitwise move) the memory, and that binary move will cause undefined behavior for all non-POD types. I don't know of any allocator implementation (POSIX, *NIX, Windows) where you can ask the system whether it will be able to grow, but that would fail if it requires moving.
Yep, you're right that the standard allocator interface doesn't provide optimizations for memcpy'able types.
It's been possible to determine whether a type can be memcpy'd using boost type traits library (not sure if they provide it out of the box or one would have to build a composite type discriminator based on the boost ones).
Anyway, to take advantage of realloc() one would probably create a new container type that can explicitly take advantage of this optimization. With current standard allocator interface it doesn't seem to be possible.

worst that can happen if i don't obey the stateless custom allocators dogma?

I need to create a custom allocator for std:: objects (particularly and initially for std::vector) but it might eventually come to use others
The reason I need to create a custom allocator is that I need to track allocated (heap & stack) resources by individual components of the application (this is an inherent feature of the application). I will need the custom allocator to monitor the heap portion of the resources, so it is essential that I'm able to pass to the std::vector constructor something like
trackerId idToTrackUsage;
myAlloca<int> allocator(idToTrackUsage);
vector<int> Foo( allocator );
However, after reading a bit I found this little bomb about the STL / C++ standard (see references) saying that all allocator instances of a given type should be equivalent (that is that == should return true for any two instances) and, most terminal; any allocator should be able to deallocate memory allocated by any other instance (that is, without having a way to know what that other instance might be). In short, allocators cannot have state.
So I'm trying to find the best way around this. Any clever ideas? I really really REALLY don't want to have to keep a custom version of std::vector around.
EDIT: i read about scoped allocators for c++0x on http://www2.research.att.com/~bs/C++0xFAQ.html#scoped-allocator but i couldn't really get far into understanding how this applies to my problem. If anyone thinks c++0x alleviates this problem, please comment
References:
Allocator C++ article in Wikipedia
Some random further reading courtesy of Google
Aside from the obvious answer ("if you violate any requirement, that's undefined behavior, good night and thanks for playing"), I imagine the worst that would likely happen, is that the vector implementation can rely on the requirement that "all instances of the allocator class are interchangeable" in the obvious way:
vector(const Allocator &youralloc = Allocator()) {
const Allocator hawhaw;
// use hawhaw and ignore youralloc.
// They're interchangeable, remember?
}
Looking at the source, GCC's vector implementation (which I think is based eventually on SGI's original STL implementation) does sort-of store a copy of the allocator object passed into that constructor, so there's some hope that this won't happen.
I'd say try it and see, and document what you've done very carefully, so that anyone attempting to use your code on an implementation that you haven't checked, knows what's going on. Implementers are encouraged in the standard to relax the restrictions on allocators, so it would be a dirty trick to make it look as though they're relaxed when really they aren't. Which doesn't mean it won't happen.
If you're really lucky, there's some documentation for your container implementation that talks about allocators.
You could, of course, leave a pointer to whatever state you need in any allocated blocks. This does of course mean that any per-block state must be stored in that block, and the allocator instances would act more like handles than actual objects in as of themselves.
Making the allocator state static would do the trick, if you're able to work with that. It does mean that all allocators of that type will have to share their state, but from your requirements, that sounds like it could be acceptable
To respond to your edit: yes, in C++0x or C++11, allocators can have state.

How to use std::allocator in my own container class

I am trying to write a container class which uses STL allocators. What I currently do is to have a private member
std::allocator<T> alloc_;
(this will later be templated so that the user can pick a different allocator) and then call
T* ptr = alloc_.allocate(1,0);
to get a pointer to a newly allocated 'T' object (and used alloc_.construct to call the constructor; see the answer below). This works with the GNU C++ library.
However, with STLPort on Solaris, this fails to do the right thing and leads to all sorts of bizarre memory corruption errors. If I instead do
std::allocator_interface<std::allocator<T> > alloc_;
then it is all working as it should.
What is the correct way to use the stl::allocator? The STLPort/Solaris version fails to compile with g++, but is g++ right?
You need to both allocate and construct with the allocator. Something like this:
T* ptr = alloc_.allocate(1,0);
alloc_.construct(ptr, value);
Lots of things are downright broken if you don't start with a properly constructed object. Imagine a std::string being allocated but not constructed. When you try to assign to it, it will first try to cleanup its old contents by freeing some data, which will of course be garbage values from the heap and crash.
Something you might want to do is have your own custom allocator that you can use to see how the standard containers interact wit allocators. Stephan T. Lavavej posted a nice, simple one called the mallocator. Drop it into a test program that uses various STL containers and you can easily see how the allocator is used by the standard containers:
http://blogs.msdn.com/vcblog/archive/2008/08/28/the-mallocator.aspx
Not all of the interface functions in the mallocator (such as construct() and destroy()) are instrumented with trace output, so you might want to drop trace statements in there to more easily see how the standard containers might use those functions without resorting to a debugger.
That should give you a good idea of how your containers might be expected to use a custom allocator.