Sign in to follow this  
bullfrog

C++ Memory Usage & Allocation

Recommended Posts

bullfrog    481
Hi,

I am currently working on a game and I am having some difficulty understanding why my memory usage is so high.

So for testing I created a simple project to that allocates memory.

[CODE]
#include <iostream>

unsigned int g_uiTotalClusterMemory = 0;
unsigned int g_uiTotalCubeMemory = 0;

class CCube
{
public:
CCube() {}
~CCube() {}

private:
int m_i1;
int m_i2;
};

class CCluster
{
public:
CCluster()
{
for (int i = 0; i < s_kiX; ++i)
{
for (int j = 0; j < s_kiY; ++j)
{
for (int k = 0; k < s_kiZ; ++k)
{
m_pCube[i][j][k] = new CCube();
g_uiTotalCubeMemory += sizeof(CCube);
}
}
}
}

~CCluster() {}

private:
static const int s_kiX = 16;
static const int s_kiY = 16;
static const int s_kiZ = 16;
CCube* m_pCube[s_kiX][s_kiY][s_kiZ];
};

CCluster* g_pCluster = 0;

void main()
{
const int kiNumClusters = 16 * 16 * 16;

//Instance clusters
g_pCluster = new CCluster[kiNumClusters];

//Calculate memory useage for all clusters
g_uiTotalClusterMemory = sizeof(CCluster) * kiNumClusters;

//Convert bytes to megabytes
g_uiTotalClusterMemory /= 1024;
g_uiTotalClusterMemory /= 1024;
g_uiTotalCubeMemory /= 1024;
g_uiTotalCubeMemory /= 1024;

//Calculate total memory
unsigned int g_uiTotalMemory = g_uiTotalClusterMemory + g_uiTotalCubeMemory;

//Output memory values to screen
std::cout << "Total Cube Memory Used:" << g_uiTotalCubeMemory << "\n";
std::cout << "Total Cluster Memory Used:" << g_uiTotalClusterMemory << "\n";
std::cout << "Total Memory Used:" << g_uiTotalMemory << "\n";

//Pause
float fCaek = 0.0f;
std::cin >> fCaek;
}
[/CODE]

The code outputs the following:

Total Cube Memory Used:128
Total Cluster Memory Used:64
Total Memory Used:192

But windows taskmanager and process explorer both tell me the program is using 329,624k of memory. Is this test setup correctly? If so why is my program using more memory then I am allocating?

Thanks!

EDIT: I am using Visual Studio 2010 with the program running outside of the IDE, release build.

Share this post


Link to post
Share on other sites
wqking    761
16 * 16 * 16 = 4K
sizeof(CCluster) = 4K * 2 * sizeof(int)
You are allocating 4K * sizeof(CCluster) = 4K * 4K * 2 * sizeof(int) = 128M memory.
It's not surprise to see task manager reporting 300M memory usage.

Share this post


Link to post
Share on other sites
bullfrog    481
[quote name='wqking' timestamp='1331108780' post='4920007']
16 * 16 * 16 = 4K
sizeof(CCluster) = 4K * 2 * sizeof(int)
You are allocating 4K * sizeof(CCluster) = 4K * 4K * 2 * sizeof(int) = 128M memory.
It's not surprise to see task manager reporting 300M memory usage.
[/quote]


From what I understand and what the program tells me,

16 * 16 * 16 = 4096 Clusters
4096 * 16 * 16 * 16 = 16777216 Cubes

16777216 * (sizeof(Cube*)) = 64MB
16777216 * (sizeof(int) * 2) = 128MB, like you said

329 - (64 + 128) = 137MB memory that is unaccounted for?

I am using Process Explorer as well to check the programs memory footprint.

Share this post


Link to post
Share on other sites
wqking    761
[quote name='bullfrog' timestamp='1331109417' post='4920008']
329 - (64 + 128) = 137MB memory that is unaccounted for?

[/quote]
Your memory is not allocated one byte after another.
There are maybe holes in your memory.
Those holes have to be counted in task manager.
It's quite reasonable that task manager reporting twice or triple times memory than you allocated.

Share this post


Link to post
Share on other sites
bullfrog    481
[quote name='wqking' timestamp='1331109915' post='4920009']
[quote name='bullfrog' timestamp='1331109417' post='4920008']
329 - (64 + 128) = 137MB memory that is unaccounted for?

[/quote]
Your memory is not allocated one byte after another.
There are maybe holes in your memory.
Those holes have to be counted in task manager.
It's quite reasonable that task manager reporting twice or triple times memory than you allocated.
[/quote]


I see what you are saying. I will do another test with allocated blocks of 16 cubes instead of 1 by 1.

Thanks!

Share this post


Link to post
Share on other sites
wqking    761
[quote name='bullfrog' timestamp='1331112841' post='4920017']
I see what you are saying. I will do another test with allocated blocks of 16 cubes instead of 1 by 1.
[/quote]
You don't need to do more test, especially test with task manager or any process inspector.
That makes very few sense.

What you should focus on,
1, Check and avoid memory leak,
2, If the memory usage is still huge, check when and reduce the memory usage.

Share this post


Link to post
Share on other sites
bullfrog    481
[quote name='wqking' timestamp='1331114192' post='4920020']
[quote name='bullfrog' timestamp='1331112841' post='4920017']
I see what you are saying. I will do another test with allocated blocks of 16 cubes instead of 1 by 1.
[/quote]
You don't need to do more test, especially test with task manager or any process inspector.
That makes very few sense.

What you should focus on,
1, Check and avoid memory leak,
2, If the memory usage is still huge, check when and reduce the memory usage.
[/quote]

Allocating cube by cube seems to be the problem. When I allocated the cubes in batches, the memory now settles at expected levels. Windows memory aligning must not be the best.

Thank you for everyones help!

Share this post


Link to post
Share on other sites
rip-off    10976
Windows is probably doing fine here. There are multiple "layers" that affect memory allocation, any one of which could be contributing to this result.

The first layer is the type itself Padding between members, padding at the end of a structure/class and the typical implementation of virtual functions add bytes to the allocation. Fortunately we can see these via sizeof().

Another layer is for dynamically allocated arrays (via new[]). The compiler must call the destructor for all the objects in the array. To do this, it must understand the length of the array. A typical implementation will add a hidden word before the data where the length will be stored.

The above is all decided at compile time.

During execution, the language has emitted calls to the runtime (i.e. the implementation of operator new, malloc, etc). Generally speaking, the runtime requests big batches of memory from the operating system. The runtime splits this memory into smaller chunks, and hands it out. It also has to maintain some bookkeeping information to understand which memory in a chunk is allocated and when it can be freed.

Here is where most "Task Manager" analysis breaks down. From the task manager's point of view - your program is a black box. It cannot determine if memory in your program is "logically" free, nor can it give you a separate figure for the bookkeeping overhead.

Then there debug helpers, which might insert guard bytes in various places, allowing some kinds of buffer overruns to be detected. Again, the Task Manager cannot help you here. Shouldn't be occurring if you are running a Release executable by itself, but it is interesting to remember that it could be there other times.

Share this post


Link to post
Share on other sites
Adam_42    3629
The main thing that's causing your extra memory overhead is that memory allocators add some overhead to the size of allocations. Even in a release build I'd expect to see at least an additional 8 bytes per allocation, plus a rounding up to the next biggest multiple of 8 bytes for alignment. This means allocating objects that are 8 bytes (or less) each will probably actually allocate 16 bytes each. The extra data is used by the heap to track what memory is free and what isn't.

In debug the overhead can be much more, to allow better detection of heap corruption.

In your case you could simply allocate one large array of CCube objects (by changing the array definition to[font=monospace] [/font]CCube m_Cube[s_kiX][s_kiY][s_kiZ]; ). That not only eliminates almost all of the allocation overhead, but also gets rid of the extra pointer per cube (which adds 4 bytes of overhead to the 8 bytes per cube)!

In general the standard solution is to pool the allocations. This should also improve performance as you get better data locality, and therefore less cache misses.

Share this post


Link to post
Share on other sites
Zael    154
[quote name='bullfrog' timestamp='1331105853' post='4920003']
//Convert bytes to megabytes
g_uiTotalClusterMemory /= 1024;
g_uiTotalClusterMemory /= 1024;
g_uiTotalCubeMemory /= 1024;
g_uiTotalCubeMemory /= 1024;
[/quote]

Um... you are converting bytes to kilobytes here. To convert to megabytes you need another division by 1024.

Share this post


Link to post
Share on other sites
c_olin    197
[quote name='Zael' timestamp='1331138723' post='4920105']
[quote name='bullfrog' timestamp='1331105853' post='4920003']
//Convert bytes to megabytes
g_uiTotalClusterMemory /= 1024;
g_uiTotalClusterMemory /= 1024;
g_uiTotalCubeMemory /= 1024;
g_uiTotalCubeMemory /= 1024;
[/quote]

Um... you are converting bytes to kilobytes here. To convert to megabytes you need another division by 1024.
[/quote]

He is dividing by 1024 twice. Just on two separate lines for each value.

Share this post


Link to post
Share on other sites
mhagain    13430
[quote name='rip-off' timestamp='1331121395' post='4920038']
<snip>awesome</snip>
[/quote]

This basically. Never work on the basis that the memory you explicitly allocate yourself is the only memory used by your program, because it's not. Any API call you make, or any call into any external library, will very probably be doing it's own memory allocations, as well as fixed overhead from bringing up the program under the OS (heap space, stack space, program loader, all kinds of mysterious gubbins). None of this is under your control - it's like being a guest in somebody's house and finding that the fridge is empty. You've only been eating a small amount so far asyou know, certainly not enough to empty the fridge, but your hosts have also been eating.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this