Measure memory consumption of creating object or executing functions in C#
Yesterday Anton Venter from New Zealand contacted me, because he read about my posts about optimization and stuff like that. And he was looking at different sorting and searching strategies he asked me how to measure execution time and memory comsumption of functions in a black box kind of way.
Timing
Well, the execution time part was answered in my previous post on HighPerformanceCounter and a more general article about measuring performance in C#.
To quickly sum those up those two posts, to time a function use something like:
-
// run a sort first to get on the fly code gen out of the way
-
sort.run(myarray);
-
//now time it
-
timer.start();
-
sort.run(mysecondarray);
-
timer.stop();
-
console.writeline(timer.elapsedmilliseconds);
Memory Consumption
After browsing the documentation I found out that it is not too hard to measure the memory usage caused by functions and/or instantiation of and object using System.GC.GetTotalMemory. BUT that's of course "just" the amount allocated in managed memory. (So it does NOT give you an idea about how much JITing those methods would cause. Just a side-comment.)
-
long m_memoryStart = 0;
-
long m_memoryMakeList = 0;
-
-
Thread.MemoryBarrier();
-
m_memoryStart = System.GC.GetTotalMemory(true);
-
-
Thread.MemoryBarrier();
-
m_memoryMakeList = System.GC.GetTotalMemory(true);
-
-
int count = testList1.Count;
For a more complete example on how to use that see my sample solution.
I inserted the Thread.MemoryBarrier() in order to absolutely prevent the compiler from reordering statements for optimizations. And the int count = testList1.Count; is there to keep testList1 alive until AFTER the second GetTotalMemory, which is abolutely necessary! Otherwise the difference between the two memory sizes will indeed be zero.
At this point in encourage you to look at my sample. Especially notice how the first execution of Array.Sort does increase memory usage, the second does not.
Should I do timing or memory tests first?
There is no right order in which you should do these. It absolutely depends on what exactly you want to time or measure. But remember. It is best to separate the testing for memory consumption and the timing. Don't try to do both with one execution of a specific function.
Important: To get accurate readings especially for timing always compile your solution to "Release" and run the .exe outside of Visual Studio!
Thanks Anton for contacting me. It was fun thinking about your question.

Hi Tobi, Thanks first of all for this generous post!! I was anticipating a much worse approach I must say, it would have gone a little like this, To partition a section of memory and monitor the use of this section during processes. That would have been me, aproaching the bull from the wrong side
not that I have the slightest idea of how to do that and would probably have lead to the creation of more memory “bubbles” (if one might say) if the use of memory exceeded the allocated bit. Implementing your approach will probably mean that I will be maintaining my sanity! Thanks again! Anton
Comment on August 27, 2007 @ 12:51:51
Hi Tobi and Anton,
Interesting stuff… I’m having to do memory measurement for our performance unit tests. I’m using GC.GetTotalMemory(false) to query the total memory allocated, but it’s got an irritating problem – it does not accurately reflect the actual amount of memory allocated.
(I’ve had the same problem, only worse, with the .NET performance counter “# Bytes in all heaps”).
(If I do a GC.GetTotalMemory(true) I get an accurate picture, but that’s quite invasive, because it forces a GC).
If I store the results of GC.GetTotalMemory(false) in an array:
byte[][] cache = new byte[1000][];
long[] memTotals = new long[1000];
for (int i = 0; i
then dump out memTotals, apparent increases only occur once every 8 allocations - i.e. GC.GetTotalMemory(false) only seems to register an increase in ~8192 byte steps...
Given that this effect is (hopefully) deterministic, and tiny, I'm still going to plough ahead and use GC.GetTotalMemory in my instrumentation, but it's a pain in the arse to know that it's not 100% reliable... is full profiling the only totally accurate way to do it, or am I talking crazy?
Comment on December 11, 2007 @ 16:59:04
sorry… tags cut off code. Was:
byte[][] cache = new byte[1000][];
long[] memTotals = new long[1000];
for (int i = 0; i
Comment on December 11, 2007 @ 17:00:42
Hi Andy,
please email me the complete code, since my comments seem to not like any less-than signs. I really have to fix that.
sorry.
But the good thing is: I think I know what happens – but the rest of your code is quite important for me finding out what exactly is happening. So please, send me the code…
You can find my email address on the “about me” page.
Cheers,
Tobi
Comment on December 12, 2007 @ 16:49:30
Hello,
I’m facing the same problem as Andy. I’m looking for the piece of code that puts my memory in 500MB when I make a loop getting data from my database with NHibernate. I’m using GC.TotalMemory(true) but it never uses the same amount of memory after I open a session.
I hope you can help me with that.
Thank you.
Comment on August 18, 2008 @ 17:00:54
Hey Bruon,
could you email me a codesample? That would be easier for me to comment on, since I don’t fully understand the circumstances where you see your varying memory sizes.
Cheers.
Comment on August 19, 2008 @ 12:48:14
Hello Tobias,
I think the problem is gone now. Thanks for your answer. Could you please point me a good book in the matter?
Thanks again,
Bruno
Comment on August 21, 2008 @ 12:37:47
Hey Bruno,
unfortunatelly there is little information out there about memory management in C# and no book I am aware of. Some links:
http://blog.vuscode.com/malovicn/archive/2007/12/30/net-foundations-stack-and-heap.aspx
http://www.bluebytesoftware.com/blog/
http://msdn.microsoft.com/en-us/library/f144e03t.aspx
Other than that… look at the mono runtime source code.
Cheers,
Tobi
Comment on August 22, 2008 @ 12:42:58