C++ STL question: allocators - c++

I have a (potentially dumb) question about the C++ STL. When I make a container (vector, set, map, etc), is it allocated on the stack or on the heap? If I make a set and put 5 million strings, will I have to worry about a stack overflow?

STL classes by default allocate their internal buffers from the heap, although these classes also allow custom allocators that allow a user to specify an alternate location to allocate from - e.g. a shared memory pool.

The default allocator for STL containers uses operator new and delete, so it's whatever those route to for the type being contained. (In general, it comes from the heap unless you do something to override that.)
You will not get a stack overflow from allocating 5 million strings. Even if you made a stack based allocator, it would probably overflow before you even inserted one string.

The container itself is allocated where you decide (it can be the stack, the heap, an object's member, etc) but the memory it uses is, by default, as others described, taken on the Free Store (managed through new and delete) which is not the same as the heap (managed through malloc/free).
Don't mix the two!

Related

Is it safe to "dissolve" c++ arrays on the heap?

I am currently implementing my own vector container and I encountered a pretty interesting Issue(At leas for me). It may be a stupid question but idk.
My vector uses an heap array of pointers to heap allocated objects of unknown type (T**).
I did this because I wanted the pointers and references to individual elements to stay same, even after resizing.
This comes at performance cost when constructing and copying, because I need to create the array on the heap and each object of the array on the heap too. (Heap allocation is slower than on the stack, right?)
T** arr = new *T[size]{nullptr};
and then for each element
arr[i] = new T{data};
Now I wonder if it would be safe, beneficial(faster) and possible, if instead of allocating each object individually, I could create a second array on the heap and save the pointer of each object in the first one.Then use (and delete) these objects later as if they were allocated separately.
=> Is allocating arrays on the heap faster than allocating each object individually?
=> Is it safe to allocate objects in an array and forgetting about the array later? (sounds pretty dumb i think)
Link to my github repo: https://github.com/LinuxGameGeek/personal/tree/main/c%2B%2B/vector
Thanks for your help :)
First a remark, you should not think of the comparison heap/stack in terms of efficiency, but on object lifetime:
automatic arrays (what you call on stack) end their life at the end of the block where they are defined
dynamic arrays (whay you call on heap) exists until they are explicitly deleted
Now it is always more efficient to allocate a bunch of objects in an array than to allocate them separately. You save a number of internal calls and various data structure to maintain the heap. Simply you can only deallocate the array and not the individual objects.
Finally, except for trivially copyable objects, only the compiler and not the programmer knows about the exact allocation detail. For example (and for common implementations) an automatic string (so on stack) contains a pointer to a dynamic char array (so on heap)...
Said differently, unless you plan to only use you container for POD or trivially copyable objects, do not expect to handle all the allocation and deallocation yourself: non trivial objects have internal allocations.
Heap allocation is slower than on the stack, right?
Yes. Dynamic allocation has a cost.
Is allocating arrays on the heap faster than allocating each object individually?
Yes. Multiple allocations have that cost multiplied.
I wonder if it would be ... possible, if instead of allocating each object individually, I could create a second array on the heap and save the pointer of each object in the first one
It would be possible, but not trivial. Think hard how you would implement element erasure. And then think about how you would implement other features such as random access correctly into the container with arrays that contain indices from which elements have been erased.
... safe
It can be implemented safely.
... beneficial(faster)
Of course, reducing allocations from N to 1 would be beneficial by itself. But it comes at the cost of some scheme to implement the erasure. Whether this cost is greater than the benefit of reduced allocations depends on many things such as how the container is used.
Is it safe to allocate objects in an array and forgetting about the array later?
"Forgetting" about an allocation seems like a way to say "memory leak".
You could achieve similar advantages with a custom "pool" allocator. Implementing support for custom allocators to your container might be more generally useful.
P.S. Boost already has a "ptr_vector" container that supports custom allocators. No need to reinvent the wheel.
I did this because I wanted the pointers and references to individual
elements to stay same, even after resizing.
You should just use std::vector::reserve to prevent reallocation of vector data when it is resized.
Vector is quite primitive, but is is highly optimized. It will be extremely hard for you to beat it with your code. Just inspect its API and try its all functionalities. To create something better advanced knowledge of template programing is required (which apparently you do not have yet).
What you are trying to come up with is a use of placement new allocation for a deque-like container. It's a viable optimization, but usually its done to reduce allocation calls and memory fragmentation, e.g. on some RT or embedded systems. The array maybe even a static array in that case. But if you also require that instances of T would occupy adjacent space, that's a contradicting requirement, resorting them would kill any performance gains.
... beneficial(faster)
Depends on T. E.g. there is no point to do that to something like strings or shared pointers. Or anything that actually allocates resources elsewhere, unless T allows to change that behaviour too.
I wonder if it would be ... possible, if instead of allocating each
object individually, I could create a second array on the heap and
save the pointer of each object in the first one
Yes it is possible, even with standard ISO containers, thanks to allocators.
There is concern of thread safety or awareness if this "array" appears to be shared resource between multiple writer and reader threads. You might want to implement thread-local storages instead of using shared one and implement semaphores for crossover cases.
Usual application for that is to allocate not on heap but in statically allocated array, predetermined. Or in array that was allocated once at start of program.
Note that if you use placement new you should not use delete on created objects, you have to call destructor directly. placement new overload is not a true new as far as delete concerned. You may or may not cause error but you certainly will cause an crash if you used static array and you will cause heap corruption when deleting element that got same address as dynamically allocated array beginning
This comes at performance cost when constructing and copying, because I need to create the array on the heap and each object of the array on the heap too.
Copying a POD is extremely cheap. If you research perfect forwarding you can achieve the zero cost abstraction for constructors and the emplace_back() function. When copying, use std::copy() as it is very fast.
Is allocating arrays on the heap faster than allocating each object individually?
Each allocation requires you to ask the operating system for memory. Unless you are asking for a particularly large amount of memory you can assume each request will be a constant amount of time. Instead of asking for a parking space 10 times, ask for 10 parking spaces.
Is it safe to allocate objects in an array and forgetting about the array later? (sounds pretty dumb i think)
Depends what you mean by safe. If you can't answer this question on your own, then you must cleanup the memory and not leak under any circumstance.
An example of a time you might ignore cleaning up memory is when you know the program is going to end and cleaning up memory just to exit is kinda pointless. Still, you should clean it up. Read Serge Ballesta answer for more information about lifetime.

Why would I write custom allocators for STL containers when I could just overwrite new and delete?

I want to write a custom memory manager/allocator for learning. I'm tempted to have a master allocator that requests n bytes of ram from the heap (via new). This would be followed by several allocator... Adaptors? Each would interface with the master, requesting a block of memory to manage, these would be stack, linear, pool, slab allocators etc each managing allocations from their slice of the master pool allocator.
The problem I have is whether I should write custom allocator_traits to interface with these for the various STL containers; or if I should just ignore the adaptor idea and simply overload new and delete to use the custom pool allocator/manager, the master one.
What I'm interested in understanding is what tangible benefit I would gain from having separate allocators for STL containers? It seems like the default std::allocator calls new and delete as needed so if I overload those to instead request from my big custom memory pool, I'd get all the benefit without the kruft of custom std::allocator code.
Or is this a matter where certain types of allocator models, like using a stack allocator for a std::deque would work better than the default allocator? And if so, wouldn't the normal stl implementation already specialise the default allocator for the various container types, or otherwise be optimised in the calls to the default allocator?
If it matters at all, I'm using C++20 via GCC 10+
If you want to replace the global allocator, including in every library you are using, you don't have to use std::allocator.
std allocators let you do things like create temporary allocation pools. Suppose you have some data structures you can guarantee will not outlive a certain scope, and you know that (whatever is allocated) 90%+ will remain allocated to the end of the scope.
A relatively simple std allocator could hand outmemory, never recycle it, and clean it up at the end of the scope much faster than any global new or delete operator could.
Whenever you have special knowledge of the contents and lifetime patterns of a container, you could hand-tune an allocator for that specific container. The standard allocator cannot. Sometimes when you are willing to make compromises that the std containers are not, you can patch their behavior with a custom allocator.
std::deque cannot efficiently use a stack allocator, because it cannot presume you'll mainly use it as a stack. You might use it mainly a queue. A stack allocator when you use it mainly as a queue would be a disaster; but if you used it 90%+ as a stack, a stack allocator could be much faster at the cost of modest memory overhead (and if 99%+, a stack allocator that handles the exceptional case and cleans up the non-stack based operations).
Finally, allocators can permit you to distinguish between kinds of containers. You might want the memory for your document (persistent) state to be allocated in one region of memory, and your "scratch" non-persistent data to be allocated elsewhere.
And yes, using a std allocator is something you should consider not doing. Optimization is fungible, and tweaking low level memory allocation is something you can work on after you have made the rest of the system more efficient and functional. Only when you have something that works, isn't fast enough, and you have identified new/delete as a fundamental bottle neck you can't design around should you say "ok, time to replace allocation!"
Use Case: Security Software needs to shred memory on delete, 'cause it cannot afford to let sensitive data remain somewhere in the physical RAM, optionally accessible by later instantiated processes. The delete operators of standard run-times won't do this expensive operation. Overwriting the heap operators might lead to linker problems with libraries depending on the runtime versions of those.
Answering the two questions in-order:
Should I write custom allocator_traits to interface my allocators for the various STL containers?
Yes, for easy manipulations. Pretty soon in the implementation, situations such as controlling memory overlaps would arise. For example, while stress-testing the implementation at full capacity of individual allocators and figuring out an algorithm for re-allocation. In this regard, you would need to specialize the allocator_traits class for the allocators rather than implement its member types from scratch using new and delete operators.
The reason allocator_traits is used is because it facilitates easy handling of certain rules that need to be respected. Such rules occur all across memory management. [Refer here for three such rules during allocator construction.]
What tangible benefit I would gain from having separate allocators for STL containers?
Absolute control of how the master allocator assigns, re-assigns, copies, moves, and destructs memory (with added controls over quantifying/enhancing performance). Pretty cool, isn't it! If the default std allocator is used, you would loose this control and rely on a (albeit very good) default implementation of memory management.

Difference between an allocator and a built in array in c++?

I have lately been trying to create custom containers that are similar to some of the library containers (i.e vector, list). and while I was using an allocator to allocate dynamic memory I noticed that the idea behind allocators and built in arrays are the same. allocators reserve a certain amount of raw, unconstructed dynamic memory and return a pointer to the first free location in that pool of memory. and built in arrays pretty much do the same thing. so if we have an std::allocator for strings called alloc
this codealloc.allocate(7) and this code string* array = new string[7] should have the same effect. and if we want to construct the raw memory we can call std::allocator::construct passing it the pointer returned from the allocate function, or we can have something like array[0] = string("something") to do the same thing. correct?
so what is there a difference between how an allocator work and how a built in array work?
You're right that they're fundamentally related, but not in that way. new string[7] could indeed be decomposed into allocate and construct (with a few extra bits for EH and other details).
Separating them out in the allocator interface allows much more fine-grained control for containers so that they can, for example, have memory with non-constructed objects in them, which is often vital for correct performance guarantees or semantics.
Additionally, The allocator interface is, of course, an interface with many possible implementations, such as memory arenas or object pools, which new string[7] really doesn't offer.
Finally, new T[] is shit and don't ever use it. The allocator interface is designed to be used only by fairly experienced programmers in quite limited ways- as a component of a better library component. new T[] is a language feature that everybody can just use, with terrible results.
An array is collinear container of slots for items in memory. The array is a range.
An allocator is an function object (or function) that reserves memory. The allocator can designate space from an array, stack, heap, or other areas of memory. The allocator can also be used to allocate space outside of the memory area, such as a hard drive or other device (maybe a server, cloud, etc.)
The space allocated for an array is usually determined by the compiler during the build phase.
An allocator is used for dynamic (during run-time) allocation of objects.

Does a vector (container) need to use an "allocator"?

I was looking into how custom containers are created, such as eastl's container and several other models and I see that they all use an "allocator", much like std::vector does with std::allocator. Which got me thinking, why do new implementations of a vector container use an allocator when they typically have an underlying memory management override for new and delete?
Being able to replace operator new() and operator delete() (and their array versions) at program level may be sufficient for small program. If you have programs consisting of many millions lines of code, running many different threads this isn't at all suitable. You often want or even need better control. To make the use of custom allocators effective, you also need to be able to allocate subobjects using the same objects as the outer allocator.
For example, consider the use of memory arena to be used when answering a request in some sort of a server which is probably running multiple threads. Getting memory from operator new() is probably fairly expensive because it involves allocating a lock and finding a suitable chunk of memory in a heap which is getting more and more fragmented. To avoid this, you just want to allocate a few chunks of memory (ideally just one but you may not know the needed size in advance) and put all objects there. An allocator can do this. To do so, you need to inform all entities allocating memory about this chunk of memory, i.e. you need to pass the allocator to everything possibly allocating memory. If you allocate e.g. a std::vector<std::string, A> the std::string objects should know about the allocator: just telling the std::vector<std::string, A> where and how to allocate memory isn't enough to avoid most memory allocations: you also need to tell it to the std::string (well, actually the std::basic_string<char, std::char_traits<char>, B> for a suitable allocator type B which is related to A).
That is, if you really mean to take control of your memory allocations, you definitely want to pass allocators to everything which allocates memory. Using replaced versions of the global memory management facilities may help you but it is fairly constrained. If you just want to write a custom container and memory allocation isn't much of your concern you don't necessarily need to bother. In big systems which are running for extensive periods of time memory allocation is one of the many concerns, however.
Allocators are classes that define memory models to be used by Standard Library containers.
Every Standard Library container has its own default allocator, However the users of the container can provide their own allocators over the default.
This is for additional flexibility.
It ensures that users can provide their own allocator which provides an alternate form of memory management(eg: Memory Pools) apart from the regular heap.
If you want to produce a standard-compatible container then the answer is of course yes... allocators are described in the standard so they are required.
In my personal experience however allocators are not that useful... therefore if you are developing a container for a specific use to overcome some structural limitation of the standard containers then I'd suggest to forget about allocators unless you really see a reason for using them.
If instead you are developing a container just because you think you can do better than the standard vector then my guess is that you are wasting your time. I don't like the allocator idea design (dropping on the type something that shouldn't be there) but luckily enough they can be just ignored. The only annoyance with allocators when you don't need them (i.e. always) is probably some more confusion in error messages.. that however are a mess anyway.

Second argument to std::vector

Looking at vector, I realized that I have never used the second argument when creating vectors.
std::vector<int> myInts; // this is what I usually do
std::vector<int, ???> myOtherInts; // but is there a second argument there?
Looking at the link above it says that it is for:
Allocator object to be used instead of constructing a new one.
or, as for this one:
Allocator: Type of the allocator object used to define the storage allocation model. By default, the allocator class template for type T is used, which defines the simplest memory allocation model and is value-independent.
I guess it has to do with something with memory management. However, I am not sure how to use that.
Any pointers regarding this?
The default allocator, std::allocator<>, will handle all allocations made by std::vector<> (and others). It will make new allocations from the heap each time a new allocation is needed.
By providing a custom allocator, you can for instance allocate a big chunk of memory up front and then slice it up and hand out smaller pieces when separate allocations are needed. This will increase the allocation speed dramatically, which is good for example in games, at the cost of increased complexity as compared to the default allocator.
Some std type implementations have internal stack-based storage for small amounts of data. For instance, std::basic_string<> might use what is called a small string optimization, where only strings longer than some fixed length, say 16 characters (just an example!), gets an allocation from the allocator, otherwise an internal array is used.
Custom allocators are rarely used in general case. Some examples of where they can be useful:
Optimization for a specific pattern of allocations. For example, a concurrent program can pre-allocate a large chunk of memory via standard means at the beginning of task execution and then shave off pieces off it without blocking on the global heap mutex. When task is completed, entire memory block can be disposed of. To use this technique with STL containers, a custom allocator can be employed.
Embedded software, where a device has several ranges of memory with different properties (cached/noncached, fast/slow, volatile/persistent etc). A custom allocator can be used to place objects stored in an STL container in a specific memory region.
Maybe this will help: http://www.codeguru.com/cpp/cpp/cpp_mfc/stl/article.php/c4079
You may try google for: stl allocator.
Allocators (STL) help you to manage memory for your objects in vector class. you may use the custom allocator for different memory model( etc).
Hi you can find example of custom allocator http://www.codeproject.com/KB/cpp/allocator.aspx