views:

86

answers:

3

I have a part of code that operates on large arrays of double (containing about 6000 elements at least) and executes several hundred times (usually 800) .

When I use standard loop, like that:

double[] singleRow = new double[6000];
int maxI = 800;
for(int i=0; i<maxI; i++)
{
singleRow = someObject.producesOutput();
//...
// do something with singleRow
// ...
}

The memory usage rises for about 40MB (from 40MB at the beggining of the loop, to the 80MB at the end).

When I force to use the garbage collector to execute at every iteration, the memory usage stays at the level of 40MB (the rise is unsignificant).

double[] singleRow = new double[6000];
int maxI = 800;
for(int i=0; i<maxI; i++)
{
singleRow = someObject.producesOutput();
//...
// do something with singleRow
// ...
GC.Collect()
}

But the execution time is 3 times longer! (it is crucial)

How can I force the C# to use the same area of memory instead of allocating new ones? Note: I have the access to the code of someObject class, so if it would be needed, I can change it.

+3  A: 

Make singleRow a parameter and pass it in to the call to producesOutput every time...

Basically your producesOutput method is probably allocating a new array every time, and the re-assignment of singleRow just marks the old memory as available to remove, but doesn't run the GC for performance reasons.

John Weldon
No need to make it a ref if the array is of fixed length.
Steven Sudit
That's true. Thanks for the reminder :)
John Weldon
Arguably, `ref` would cause no direct harm and would show the intent to modify the parameter. Still, I don't think it's the right thing to do.
Steven Sudit
@Steven, using ref seems to be a little bit clearer.@John, Thanks for your help. Pity I cannot accept two answers ;)
Gacek
Well, `ref` would be needed if the array was to be replaced by another one as opposed to being modified in place. Since you really do want to modify in place, the `ref` might be misleading. The deeper confusion is that it allows the method to do something that it, in fact, never does. (I upvoted John's answer, too.)
Steven Sudit
I need to check the convention used in our project. I guess I will leave the ref with a proper comment to explain the usage.
Gacek
+5  A: 

Why are you allocating a large, empty singleRow only to overwrite it? Maybe you should be passing the array in to have its values modified in place. This would allow you to reuse it.

double[] singleRow = new double[6000];
int maxI = 800;
for(int i=0; i<maxI; i++)
{
    someObject.FillWithOutput(singleRow);
    //...
    // do something with singleRow
    // ...
}

If the method sometimes fills less than 6000 elements, it could simply return the fill count. Alternately, you could use a List<double>, which will allow resizing.

Steven Sudit
It seems the solution was that simple.. ;) Thanks! It works.
Gacek
Glad our advice helped.
Steven Sudit
A: 

You aren't going to like this, but if you have to force GC you're doing something wrong. Keep in mind that memory may grow until there's pressure to trigger a GC - this is a GOOD thing because it means GC doesn't run until it has to.

Here's a silly-sounding test, but it might shed some light on what is happening. Inside FillWithOutput() comment out most of its functionality. Then run your loop and measure memeory. Incrementally un-comment out pieces of it until you see a blip. Now your're getting closer to what is causing the 'leak'.

n8wrl
I think the source of the "leak" is pretty clear already.
Steven Sudit