c++ memory management

Started by
14 comments, last by David Neubelt 16 years, 9 months ago
The new and delete that you use are defined by the platform. Really the OS can do whatever it wants when it is asked to allocated some memory, so long as new and delete behave as expected. A new call filters down to the operating system kernel allocation call which returns the pointer back up to new which returns it to you. We can do very little about what happens when you request allocation, be it malloc, realloc etc or new.

What we can do however is request a big block of memory (that we think is going to be big enough for our application) and managed it ourselves. This is tricky, but definately doable, but doesn't provide a great deal of benefits to the average application. The default implementation of new and underlying memory management is fast and efficient, but is designed to be very general purpose. However it can be improved upon slightly.

An example of this is how my memory manager works. I wrote this such that it saved memory and was quicker at allocating than the default strategy. I allocated a pool when the MM initialises and then partition that pool off into my own pages.

**FYI when you make an allocation with new there is a 32 byte header on the front of it that contains information about it's sizeand such. This is the information used by delete.**

The reason you allocate a pool is because it means that you only make an allocation through the OS (which is generally slow, though as mentioned before is still perfectly fine for 99% of applications). What you can now do is rewrite new such that it makes a call to your memory manager. The MM now 'allocates' some memory in the preallocated pool (almost instantly) and returns a pointer to it, which propogates to the pointer you are assigning to in your application.

If you were to manage a simple pool that could cater for allocations of a variety of sizes, you would need to maintain a similar 32 byte header on the front of each allocation. In this scenario only faster allocation is acheived. However if you were to fragment the pool into multiple pages, some pages could be created to only store allocations of a certain size and therefore a header is not actually needed. In this scenario you have fast allocation and a reduction in memory usage due to the lack of headers.

There are some interesting tasks to consider when managing a 'general pool' as i call it, that is a pool that can store different sizes of allocations. You have to maintain a linked lists that is the headers in front of the allocations. When an allocation comes in you have some checking to do. In mine i iterated through the allocation headers until i find one that was the perfect size, big enough (but not massive) or one that could be split into a partition of a certain size and the remainder. Once a suitable block is found i set it up for the allocation request. I then make a pass over the entire linked list looking for sequences of adjacent unused blocks and then merge them all togther.

Finally i would like to stress that there isn't much need to do this unless you have an application that does alot of per-frame/run-time allocations. A typical application won't do alot of this, but things such as physics simulations and research tools might well do. Also, applications that are targetted for platforms where there is little memory available (such as embedded devices) would benefit from a system like this, however even then it is important to determine whether the memory saved is worth the effort.

Hope that helps,

If you have any questions, i can try and answer!

Dave
Advertisement
Quote:Original post by MaulingMonkey
Quote:Original post by T1Oracle
Quote:Original post by jpetrie
Quote:
does this relates the different allocators one can give to STL containers ?

The allocators are used to customize how memory is allocated for SC++L containers, but not what new and delete actually end up doing. So they're somewhat related, but not really...

Do note that STL (SC++L looks overly convoluted :\ ) allocators are not guaranteed to be used by "list" and associative containers since they are node based. This is because STL allocators only allocate T type objects not the ListNode<T> or MapNode<T> objects that node based containers need.


Wrong. SC++L allocators must expose Allocator::rebind<U>::other, also an allocator, which allows an allocator to be adapted to a new type U, where U can be, say, a node type.


Quote:Original post by Scott Meyers Effective STL Page 48 paragraph 4 sentence 3
most of the standard containers never ask their associated allocator for memory. Never.
Programming since 1995.
Here is a question for you: are you more likely to have performance problems if you assume that the memory manager is slow, or if you know you're using a state of the art memory manager?

In my opinion, trying to optimize the memory manager is premature optimization at best. A better strategy is to use the default memory manager but code in a way that makes it easy to change your mind on memory allocations -- if the profiler tells you that you must.

Emil Dotchevski
http://www.revergestudios.com/reblog/index.php?n=ReCode.ReCode
Thanks for your answers.
I hadn't logged into gamedev for quite some time, which explains this late answer.

I am not thinking of writing my own MM at this time. It would be pointless as I still don't understand all the aspects of the subject.

However, I am trying to get a better grasp at what is happening under the hood when I write new or delete. From what I have read these operations are none trivial ones and can be very time consuming: finding the next free block with the most appropriate size, defragmenting the memory...

But, the thing that most struck me from all my readings on the subject, is the apparent common statement that MM through GCs give better performances. This is really puzzling me. If this is true then why continue to bother with news and deletes, if in the end it is more work and less efficient. I would like to know your opinion on the subject.
Garbage collectors can give better performance than the usual manual heap management allocators; it depends on the algorithms used and the patterns of memory access. Some garbage collectors can periodically compact the memory being used so that there is better cache coherence and fewer memory pages used. This compacting is how the garbage collection is actually accomplished, since only memory currently in use is copied and all the pointers are updated. This also simplifies allocations, since the memory cannot be fragmented after being compacted like this.

Completely avoiding dynamic memory management is even faster, but it requires more design work and imposes limitations on the system. Still, video games often have memory budgets for different systems to strictly control memory use, especially on console systems, avoiding general purpose dynamic memory allocation as much as possible (memory management might be done using pools of fixed size objects, which has very little cost but places restrictions on how the memory is used).

There doesn't seem to be any ideal garbage collector, so new and delete are still around (though they tend not to be in modern languages). Most garbage collectors require language level support for garbage collection, and C++ currently doesn't have that.
Quote:Original post by EmilDotchevski
Here is a question for you: are you more likely to have performance problems if you assume that the memory manager is slow, or if you know you're using a state of the art memory manager?


I can't speak in context of the original poster but I can speak in context for console games. If a game doesn't approach memory management from the initial design of the engine and each game then that game will have problems in the end.

The most efficient memory management strategy I've seen was for a ps2 game. The scheme was to have one guy in the studio manage memory range. If you wanted to allocate memory you'd have to go talk to the memory guy and request a certain amount of memory. He would then give you a range that you could allocate from.

-= Dave
Graphics Programmer - Ready At Dawn Studios

This topic is closed to new replies.

Advertisement