views:

1142

answers:

8

Background: I am writing a C++ program working with large amounts of geodata, and wish to load large chunks to process at a single go. I am constrained to working with an app compiled for 32 bit machines. The machine I am testing on is running a 64 bit OS (Windows 7) and has 6 gig of ram. Using MS VS 2008.

I have the following code:

byte* pTempBuffer2[3];
try
{
 //size_t nBufSize = nBandBytes*m_nBandCount;
 pTempBuffer2[0] = new byte[nBandBytes];
 pTempBuffer2[1] = new byte[nBandBytes];
 pTempBuffer2[2] = new byte[nBandBytes];
}
catch (std::bad_alloc)
{
 // If we didn't get the memory just don't buffer and we will get data one
 // piece at a time.
 return;
}

I was hoping that I would be able to allocate memory until the app reached the 4 gigabyte limit of 32 bit addressing. However, when nBandBytes is 466,560,000 the new throws std::bad_alloc on the second try. At this stage, the working set (memory) value for the process is 665,232 K So, it I don't seem to be able to get even a gig of memory allocated.

There has been some mention of a 2 gig limit for applications in 32 bit Windows which may be extended to 3 gig with the /3GB switch for win32. This is good advice under that environment, but not relevant to this case.

How much memory should you be able to allocate under the 64 bit OS with a 32 bit application?

+6  A: 

on windows 32 bit, the normal process can take 2 GB at maximum, but with /3GB switch it can reach to 3 GB (for windows 2003).

but in your case I think you are allocating contiguous memory, and so the exception occured.

Ahmed Said
+1 - never considered the fact that allocating a contiguous array can only give you the largest amount of "contiguous" memory free! Really good point.
Richard Corden
The /3GB switch is a bit dangerous. Many drivers aren't tested with the switch, so they may become unstable when the OS is limited to 1GB. It doesn't matter though, as he's running on a 64-bit Windows.
jalf
To help me understand what you rae saying, I will try and rephrase it. Are you saying that if I allocate the memory in smaller chunks I will be able to get more total memory allocated?
Bill
@Michael: That's what I said! ;)
jalf
@Bill: Probably, yes. If you allocate smaller chunks, they don't have to be contiguous, which means it's more likely that the OS will be able to find the space. As a simple example, imagine that your application is loaded exactly in the middle of the address space. Then no allocation you make can be bigger than half the address space - it is effectively cut in half. You can make two small allocations but not one big one. Now imagine you have a dozen or so chunks placed all over the place, and your address space gets cut up even smaller.
jalf
I believe this amount of memory is too large anyway, even with non-contiguous memory.
Drew Hoskins
It's probably to big to be allocated by the built-in new operator, but there's no reason that you can't allocate an entire GB (or even 100GB) under 32-bit windows. You just can't have it all mapped into your address space at one time.
Eclipse
@jalf Windows uses paged memory so being in one big chunk doesnt matter at all. Plus with 64bit programs it removes the 2gb limit allowing for excessively huge allocation sizes. (http://en.wikipedia.org/wiki/Paging)
Lodle
+7  A: 

As much as the OS wants to give you. By default, Windows lets a 32-bit process have 2GB of address space. And this is split into several chunks. One area is set aside for the stack, others for each executable and dll that is loaded. Whatever is left can be dynamically allocated, but there's no guarantee that it'll be one big contiguous chunk. It might be several smaller chunks of a couple of hundred MB each.

If you compile with the LargeAddressAware flag, 64-bit Windows will let you use the full 4GB address space, which should help a bit, but in general,

  • you shouldn't assume that the available memory is contiguous. You should be able to work with multiple smaller allocations rather than a few big ones, and
  • You should compile it as a 64-bit application if you need a lot of memory.
jalf
"you shouldn't assume that the available memory is contiguous. You should be able to work with multiple smaller allocations rather than a few big ones, and" This is wrong as windows uses paged memory and to the app it is contiguous for one large chunk
Lodle
Lodle, you are confusing address space and free address space. E.g. one of the most common forms of fragmentation is caused by your code itself. The EXE and the DLL's do not load starting at 0x00000000.
MSalters
@Lodle: Nope, the app sees a contiguous address space, but the exe, dll's, stack and static variables are all loaded at different addresses within that address space.
jalf
+1  A: 

With nBandBytes at 466,560,000, you are trying to allocate 1.4 GB. A 32-bit app typically only has access to 2 GB of memory (more if you boot with /3GB and the executable is marked as large address space aware). You may be hard pressed to find that many blocks of contiguous address space for your large chunks of memory.

If you want to allocate gigabytes of memory on a 64-bit OS, use a 64-bit process.

Michael
How do you get 1.4GB? Are you assuming a byte to be 4 byte? :p
jalf
3 * 466,560,000 . He's allocating 3 arrays.
Michael
Correct, I was hoping to be able to allocate 1.3 gigabytes of memory, and was surprised when I could not.
Bill
RE: "large address space aware" the code is in a .dll and the .dll was compiled with the lare address space aware settings. Does the whole app need to be flagged that way, or is it OK just to flag the .dll?
Bill
@Bill - the exe needs to be large address space aware
Michael
oo right, 3 arrays of course. I guess I should've spotted that :pAbout the large address aware flag, you can modify the .exe header after it has been compiled, in case you don't have access to recompiling it.
jalf
+1  A: 

You should be able to allocate a total of about 2GB per process. This article (PDF) explains the details. However, you probably won't be able to get a single, contiguous block that is even close to that large.

Naaff
+1  A: 

Even if you allocate in smaller chunks, you couldn't get the memory you need, especially if the surrounding program has unpredictable memory behavior, or if you need to run on different operating systems. In my experience, the heap space on a 32-bit process caps at around 1.2GB.

At this amount of memory, I would recommend manually writing to disk. Wrap your arrays in a class that manages the memory and writes to temporary files when necessary. Hopefully the characteristics of your program are such that you could effectively cache parts of that data without hitting the disk too much.

Drew Hoskins
+3  A: 

You can allocate as much memory as your page file will let you - even without the /3GB switch, you can allocate 4GB of memory without much difficulty.

Read this article for a good overview of how to think about physical memory, virtual memory, and address space (all three are different things). In a nutshell, you have exactly as much physical memory as you have RAM, but your app really has no interaction with that physical memory at all - it's just a convenient place to store the data that in your virtual memory. Your virtual memory is limited by the size of your pagefile, and the amount your app can use is limited by how much other apps are using (although you can allocate more, providing you don't actually use it). Your address space in the 32 bit world is 4GB. Of those, 2 GB are allocated to the kernel (or 1GB if you use the /3BG switch). Of the 2GB that are left, some is going to be used up by your stack, some by the program you are currently running, (and all the dlls, etc..). It's going to get fragmented, and you are only going to be able to get so much contiguous space - this is where your allocation is failing. But since that address space is just a convenient way to access the virtual memory you have allocated for you, it's possible to allocate much more memory, and bring chunks of it into your address space a few at a time.

Raymond Chen has an example of how to allocate 4GB of memory and map part of it into a section of your address space.

Under 32-bit Windows, the maximum allocatable is 16TB and 256TB in 64 bit Windows.

And if you're really into how memory management works in Windows, read this article.

Eclipse
+2  A: 

During the ElephantsDream project the Blender Foundation with Blender 3D had similar problems (though on Mac). Can't include the link but google: blender3d memory allocation problem and it will be the first item.

The solution involved File Mapping. Haven't tried it myself but you can read up on it here: http://msdn.microsoft.com/en-us/library/aa366556(VS.85).aspx

tombjo
+1  A: 

Sysinternals VMMap is great for investigating virtual address space fragmentation, which is probably limiting how much contiguous memory you can allocate. I recommend setting it to display free space, then sorting by size to find the largest free areas, then sorting by address to see what is separating the largest free areas (probably rebased DLLs, shared memory regions, or other heaps).

Avoiding extremely large contiguous allocations is probably for the best, as others have suggested.

Setting LARGE_ADDRESS_AWARE=YES (as jalf suggested) is good, as long as the libraries that your application depends on are compatible with it. If you do so, you should test your code with the AllocationPreference registry key set to enable top-down virtual address allocation.

bk1e
Good idea -- I will try out VMMap.
Bill