Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

The book Game Coding Complete, Fourth Edition, chapter 5 (Game Initialization and Shutdown), section Checking Memory contains this interesting code sample:

bool CheckMemory(const DWORDLONG physicalRAMNeeded, const DWORDLONG virtualRAMNeeded)
{
    MEMORYSTATUSEX status; 
    GlobalMemoryStatusEx(&status);
    if (status.ullTotalPhys < physicalRAMNeeded) 
    {
        // you don’t have enough physical memory. Tell the player to go get a 
        // real computer and give this one to his mother. 
        GCC_ERROR("CheckMemory Failure: Not enough physical memory."); 
        return false;
    }
    // Check for enough free memory.
    if (status.ullAvailVirtual < virtualRAMNeeded) 
    {
        // you don’t have enough virtual memory available.
        // Tell the player to shut down the copy of Visual Studio running in the 
        // background, or whatever seems to be sucking the memory dry. 
        GCC_ERROR("CheckMemory Failure: Not enough virtual memory.");
        return false;
    }

    char *buff = GCC_NEW char[virtualRAMNeeded]; 
    if (buff)
    {
        delete[] buff;
    }
    else
    {
        // even though there is enough memory, it isn't available in one
        // block, which can be critical for games that manage their own memory 
        GCC_ERROR("CheckMemory Failure: Not enough contiguous memory."); 
        return false;
    }
}

This raises some questions.

The first part just asks the OS (Windows) how much physical RAM is available. The curious part is the second one, which allocates a huge chunk of memory and frees it right away:

char *buff = GCC_NEW char[virtualRAMNeeded]; 
if (buff)
{
    delete[] buff;
}

The author goes on to explain:

... this function allocates and immediately releases a huge block of memory. This has the effect of making Windows clean up any garbage that has accumulated in the memory manager and double-checks that you can allocate a contiguous block as large as you need. If the call succeeds, you've essentially run the equivalent of a Zamboni machine through your system's memory, getting it ready for your game to hit the ice...

But I have my reservations on that.

"Cleaning garbage that has accumulated in the memory manager"? Really? If the game has just started, shouldn't there be no garbage?

"Making sure you can allocate a contiguous block"? In the very specific case where you are going to manage memory yourself, this would them make some sense, but still, if you do allocate a lot of memory right of the bat, you pretty much make it impossible for any other application to run in the system while yours is on.

Also, isn't this likely to force the OS to commit all that memory, and as consequence evict a lot of memory to the swap disk space, slowing your app startup a lot?

Is this really a good practice?

share|improve this question
2  
Most modern OSes will do nothing whatsoever when an app allocates a large area of memory, they use optimistic allocation and do not actually do anything until you fill the memory, I cannot imagine this doing anything more than being a potentially slow no-op –  Vality Jul 2 at 12:54
    
Does clearing a long strip of ground in the jungle, carving a radio, headphones, etc out of wood result in airplanes landing and delivering supplies? –  Dan Neely Jul 2 at 13:35
8  
Game Coding Complete contains a lot of nonsense paired with a lot of not-understanding-C++ and C-that-looks-a-bit-like-C++ (also demonstrated in this sample, checking the result of operator new for nullptr), if you allow me to say. Best thing you can do with that book is light your chimney. Allocating and freeing a large block of memory of course doesn't "clean up" memory. –  Damon Jul 2 at 15:02
    
@Damon, I didn't check, but I suspect they've at least overloaded the global new operator to return null instead of throwing bad_alloc. If they didn't, then yes, this code is even more nonsensical :P –  glampert Jul 2 at 19:26
1  
@glampert: Even assuming that's the case, operator delete is required to accept nullptr and treat it as no-op. Any global overload that doesn't do that is broken. Which means it's nonsensical either way. Just like assuming that allocating a huge block of memory and releasing it will "magically" do something good. At best, it won't do any harm (most likely, since the pages aren't even touched... otherwise it may swap out some pages from your working set which you'll need to reload later). –  Damon Jul 2 at 20:54
show 1 more comment

3 Answers 3

up vote 9 down vote accepted

One thing that pre-allocating a large chunk of memory could do, if your computer was low on RAM, might be to force the OS to free some extra space by swapping parts of other programs' memory space to the disk.

Since such swapping is generally a very slow operation that pretty much freezes your program while it's happening, there could be some advantage to ensuring that, if it's going to happen anyway, it will happen before your game starts rather than in the middle of gameplay.

That said, forcing a swap-to-disk like this is not exactly 100% reliable:

  • On many virtual memory implementations, merely allocating memory doesn't actually trigger any swapping; rather, it only happens when you first access each page of the memory you allocated. (This is certainly true on modern versions of Linux, with memory overcommit enabled, as it is by default; I don't know for sure how different versions of Windows handle it.)

    Thus, if you really want this code to "clean up memory", you should probably add a memset() call or equivalent to actually write data to every part of the array (or at least to the first physicalRAMNeeded bytes of it) before releasing it.

  • Also, forcing the OS to swap other programs out of the RAM like this doesn't mean that they'll stay out of it. Windows is a multitasking OS, and as soon as one of those swapped-out programs wants to run again and to use its swapped-out data, it will get swapped back in, potentially freezing your game again.

    Thus, this trick is generally only useful if the RAM is being eaten up by large chunks of data that are not being actively accessed (such as large documents left open in the background in some editing software). Web browsers tend to be particularly problematic in this regard, since, even if you're not actually using a web page opened in some background tab, the page may still have scripts running on it that force it to be kept in RAM.

    (There are ways for a program to actually tell the OS to lock a part of its memory space into RAM and prevent it from being swapped out, but these typically require elevated privileges. In any case, the code you posted does not appear to be using any such methods.)

Basically, this trick seems only useful in that relatively narrow borderline situation where the user would have enough RAM to run your game (and any other software actively running in the background) without swapping, but that RAM is currently filled with inactive and unused open files or other data that can and should be swapped to the disk while your game is running. In such situations, it may be effective in making your game seem smoother and more responsive, by replacing occasional small swap operations during gameplay with a single one during startup.

share|improve this answer
1  
From my limited knowledge of low level memory management, I can say that the decision of whether or not to swap a specific page is much more complex, and takes many more factors into consideration than simply the amount of memory requested and the amount of memory available. Oversimplifying this, and generally relying on suppositions is not very wise. –  Panda Pajama Jul 3 at 9:06
3  
I think it is important to remember that all of these are suppositions that may work, not work, or seem to work, but for completely unrelated reasons. As far as I know, none of the behaviors pointed out in this answer are documented, and it would be unwise to rely on them. An OS update, settings change, compiler change, or pretty much anything can make this stop working, or even have a negative impact on performance. –  Panda Pajama Jul 3 at 9:07
add comment

I don't know how old that writeup is, but I'd say it's pretty old. In modern Windows (XP and newer, but specially on 64-bit versions), I would say doing something like that will have little to negative impact on the startup of your game.

First of all, remember that the address space is virtualized for each process. As far as your process is concerned, you have the entire address space to yourself, and you will always get contiguous (virtual) memory, regardless of whether that memory is physically contiguous or not.

In fact, whether or not the memory is contiguous in physical memory is not something you have control over. The OS will choose how to allocate the physical blocks as it sees fit, and nothing you do at the application level can have effect on the benefits (if any) of having contiguous physical memory.

You do need to care about virtual memory fragmentation if you keep on allocating and freeing memory of different sizes for a very long amount of time. This is of course much less relevant with a 64-bit address space. Games don't usually run for months, so you most likely won't have to care about this, unless we're talking about long running game servers.

Even if you do allocate lots of memory of wildly different sizes in a 32-bit machine, modern memory allocations are pretty good, so it is unlikely you will get virtual memory fragmentation issues unless you're actively looking for them

To be honest with you, I don't know what that writer is going on about. Even if the address space was shared, and you did get a huge contiguous block in physical memory, by freeing it, you're saying you don't care about it anymore. There are other programs running on the background doing their own allocations, so even if your huge malloc succeeded, nobody will guarantee the next one will succeed as well.

I think that's a case of "it worked for some reason I don't really understand, so I made something up to explain it".

share|improve this answer
9  
RAM is not like a rotating hard disk where having contiguous blocks is in any way "better" than not. This is NOT true. contiguous RAM access is an order of magnitude faster than random access. RAM chips are broken down into sections for which there is a certain amount of latency to switch, and more importantly, L1 and L2 cache misses will drastically affect your performance when your memory is scattered. –  CaptainCodeman Jul 2 at 7:48
    
@CaptainCodeman: I have to accept that goes out of my field of knowledge, but in any way, as a Windows -application- programmer you have no control over whether your memory is physically contiguous or not. Asking for a huge block of memory won't change much. –  Panda Pajama Jul 2 at 7:52
    
@CaptainCodeman actually have contiguous virtual block be in contiguous mod 2^N block in RAM will speed it up due to cache line allocation –  ratchet freak Jul 2 at 7:57
8  
@CaptainCodeman Yeah, sequential DRAM access is faster, but we're talking about the virtual->physical mapping which happens in pages of 4 KB (or more), so whether that mapping is sequential or not shouldn't have much impact on memory read speeds. Plus, cache prefetching and stuff like that operates in virtual address space, not physical. –  Nathan Reed Jul 2 at 8:05
1  
@NathanReed Fair enough, and thanks for the clarification. I was just expressing that RAM access speed is not entirely uniform across the memory space, as was suggested. –  CaptainCodeman Jul 2 at 8:46
show 4 more comments

You seem to misunderstand something that is, admittedly, a little unclear in the author's wording. He's not talking about junk in the memory itself that needs to be cleaned up, he's talking about junk in the memory manager that needs to be cleaned up (i.e., the OS' implementation of new/malloc. It's worth noting that new basically just calls malloc and then calls the object's constructor, so for the sake of memory management, new and malloc are basically the same).

First, I think you need a quick primer on how memory allocation and deallocation tends to work. Malloc has a long linked list of available blocks of memory called the free chain. When you call malloc, it walks the free chain to find a block of memory that's big enough for your request. Then it cuts that block in two; one block being the size you requested, and the other block having whatever leftover bytes were available from that original big block. When you call free, it puts the block of memory you freed back on the free chain. Eventually, you end up with a free chain that only has a lot of small blocks of memory available. So when you ask for something big with malloc and it can't find anything that meets your high demands, it stops everything and runs around the free chain, looking for blocks of memory that are actually next to each other and that it can therefore combine into larger blocks of memory.

This memory manager is running in the background all the time, independently of your program; it's a part of the OS (Nope. This is the C runtime library, not the OS. See Nathan Reed's comment below.). It's possible that the user has been running their computer for days or weeks, and who knows what the free chain looks like by the time your game starts running. Thus, by allocating all the space you could possibly need and then clearing it up, you're guaranteeing that you have a big, juicy, contiguous chunk of memory waiting for you in the free chain.

Consider the alternative: If you don't know what the free chain looks like before you start the game proper (and if you don't use this technique, you really don't know what the free chain looks like), you may have a ton of very small blocks in the free chain. Then, in the middle of the game, when you ask for a block of memory that is "too big," suddenly your program will get hung up on a single allocation. The worst part is, there's no way of knowing how big "too big" is until you get there. It's all up to the state of the free chain.

So not only does this make sure that the free chain is in a state that you want it to be when your player gets into the middle of the game, you front-loaded the time it would take to do that allocation to the beginning of your program, when people are okay with it taking a little while to get itself adjusted, instead of in the middle of the game when your players may be trying to run-and-gun, and may find a sudden drop in framerate very frustrating!

share|improve this answer
5  
The heap data structure that malloc uses isn't part of the OS; it's part of the C runtime library, and each application has its own separate heap (or more than one) in its own virtual address space. At app startup the heap will naturally be in an almost pristine state. The OS-level memory management is entirely separate - it might use heaps as well, but it operates on the level of whole virtual memory pages, not individual app-level allocations, and ought to be much more robust to fragmentation issues. –  Nathan Reed Jul 2 at 19:55
    
@NathanReed Really? I guess I never quite understood that, although now that you say it it seems obvious. I suppose we can throw this theory out, then. However, I'm now out of ideas as to what GCC is getting at with this new/delete strategy. It does seem to make perfect sense if you misunderstand OS-level memory allocation, as I just did. –  user2533993 Jul 2 at 20:15
1  
"Malloc has a long linked list of available blocks of memory called the free chain. When you call malloc, it walks the free chain to find a block of memory that's big enough for your request.". You're describing one possible implementation, but not a modern one. For instance, any modern implementation has optimized paths for 4, 8 and 16 byte allocations, and another for large (e.g. >4kB) allocations. A single free chain performs horribly. –  MSalters Jul 3 at 8:19
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.