Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / .NET Framework / Performance / February 2004

Tip: Looking for answers? Try searching our database.

Reducing load on GC

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Lord Crc - 05 Feb 2004 21:35 GMT
Hi

Im making a raytracer in c#. I tried profiling my app with optimizeit
and found that after a short while, on average 60-80% of the total
time is spent in the GC. Several times it goes for a while to 100%.

I presume this is because i create a lot of temporary vectors and
other classes in my calculations.

Is there any way to help the GC so that it doesn't choke?

And yes, i've tried to reduce the number of temps created, but there's
a limit to what i can do without it severly impacting the
"codeability".

- Asbjørn
David Browne - 05 Feb 2004 22:28 GMT
> Hi
>
[quoted text clipped - 10 lines]
> a limit to what i can do without it severly impacting the
> "codeability".

One method is to replace reference objects with value objects.

Another is to recycle your temporary objects.

Replacing

Vector v = new Vector(10);
with
Vector v = MyVectorPool.Get(10);
...
MyVectorPool.Put(v);

David
Lord Crc - 05 Feb 2004 23:11 GMT
>One method is to replace reference objects with value objects.
>
>Another is to recycle your temporary objects.

so structs are better on the GC?

the problem with recycling is that it has to be thread safe, and it
seem that it kills the peformance...

- Asbjørn
David Browne - 06 Feb 2004 01:08 GMT
> >One method is to replace reference objects with value objects.
> >
> >Another is to recycle your temporary objects.
>
> so structs are better on the GC?

Yes.  They live on the stack, not the heap so they generate no garbage.
Unless they aggregate reference types like Arrays or Strings, etc.
Arrays are the killer.
It's hard to live without arrays, and arrays always live on the heap.

But so long as they contain only other value types, there's no garbage.

> the problem with recycling is that it has to be thread safe, and it
> seem that it kills the peformance...

For thread-safe pooling, nothing beats the [ThreadStatic] attribute.
It turns any static field into a ThreadStatic field, so each thread has its
own copy.

 class Vector : IDisposable
 {
   [ThreadStatic]
   static Stack pool = new Stack();
   public static Vector newVector()
   {
     if (pool.Count > 0)
       return (Vector)pool.Pop();
     else
       return new Vector();
   }
   public void Dispose()
   {
     //clean up instance fields for reuse
     //then put it back in the pool
     pool.Push(this);
   }
   //Vector implementation
 }

Then
using (Vector v = Vector.newVector())
{
 //do stuff
};

Exiting the using block will Dispose the Vector, which returns it to the
pool.

David
Niall - 09 Feb 2004 05:43 GMT
It's also important to look at the ageing behaviour of your objects. GCs of
gen 0 are fast, gen 1 are fastish and gen 2 are slow. From Rico Mariani's
site, he says a gen 0 collection every second, a gen 1 collection every 10
seconds, and (I think) a gen 2 collection every couple of minutes are ok.
Obviously this depends on your specific program situation.

Use the Performance Monitor to look at the sizes of your gen 0, gen 1, gen 2
heaps, how often the collections for each generation are happening, and how
much is promoted into the next generation. I find it hard to believe that if
the bulk of collected objects were in gen 0, your ray tracer would be
spending so much time in GC. After all, there is a lot of computationally
intensive work in a tracer - at some point it has to stop allocating memory
and actually calculate some image data.

If it is the case that you are getting collections in gen 1 and gen 2 too
often, then you need to change your code to allow objects to be released as
early as possible. The more you can keep in gen 0, the better.

Niall

> > >One method is to replace reference objects with value objects.
> > >
[quoted text clipped - 46 lines]
>
> David
Lord Crc - 09 Feb 2004 12:02 GMT
>If it is the case that you are getting collections in gen 1 and gen 2 too
>often, then you need to change your code to allow objects to be released as
>early as possible. The more you can keep in gen 0, the better.

Ok, so setting things to null as soon as im done with them is good?

- Asbjørn
Niall - 09 Feb 2004 23:32 GMT
Randomly setting to null isn't good, you need to have a specific reason to
null out a reference.

The only time it will do much good is if you have a long-lived object
holding a reference to a short lived object. In this case, if you null out
the reference to the short lived object, you make it collectible (presuming
nothing else is referencing the object).

Niall

> >If it is the case that you are getting collections in gen 1 and gen 2 too
> >often, then you need to change your code to allow objects to be released as
[quoted text clipped - 3 lines]
>
> - Asbj?rn
Lord Crc - 10 Feb 2004 21:09 GMT
>Randomly setting to null isn't good, you need to have a specific reason to
>null out a reference.

i was thinking when im done with something inside a method. some
profiling shows that gen 0 and gen 1 are fairly constant, but gen 2
just grows and grows and grows...

- Asbjørn
Niall - 11 Feb 2004 01:23 GMT
If it's a local variable inside the method, setting it to null when you're
finished with it usually doesn't help.

This is because if a GC happens in the middle of the function, the JIT can
tell if you are no longer using the object, even if its reference is still
in scope, and hence allow the GC to collect the object. I think the only
time you could outsmart the jit might be a situation like this:

SomeClass SomeObj = new SomeClass();

for (int i = 0; i < 10; i++)
{
   if (i < 5)
   {
       SomeObj.DoSomething();
       ...
   }
   else
   {
       ...
   }
}

In this case, if a GC happens during the loop, at, say i = 6, I doubt the
JIT will be able to tell that you won't be using the object any more. So
nulling it out may cause the object to be collected slightly earlier.
However, you should realise that all this hinges on when the GC collects. If
it collects after the loop, then you won't need to null out the reference.
So this code only helps if it's quite likely there will be a GC during the
loop, but after you are finished with the object.

Gen 0 and 1 must be growing in some way, because objects need to pass there
on their way through to Gen 2. How frequent are collections on Gen 0, Gen 1
and Gen 2? I think you should give the CLR Profiler a try, as Eric
suggested. You can see what types of objects are taking up all the space in
Gen 2, and work out how to reduce the number of allocations of those
objects.

I don't know how complex your tracer is, but I found from the nature of mine
that a lot of allocations are locals only, hence they die quickly. Most of
the objects that my tracer uses are very temporary - just colours, vectors,
etc. They're used for a couple of calculations, then they're useless. So
they should always die in gen 0. If you're looking to reduce the number of
allocations, you might pool simple types like vectors, and hence they will
end up in gen 2, but as long as the pool size is small, gen 2 should not
grow by much...

Niall

> >Randomly setting to null isn't good, you need to have a specific reason to
> >null out a reference.
[quoted text clipped - 4 lines]
>
> - Asbj?rn
Ice - 18 Feb 2004 11:51 GMT
can someone explain why this code leaks? this is borrowed code from another
thread.

public class Test
   {
       [STAThread]
       static void Main(string[] args)
       {
           int Iterations;
           Iterations = args.Length == 1 ? int.Parse(args[0]) : 10000;
           /* an array of arrays (jagged array) so that
            I can allocate multiple arrays in the test loop
            If there is a problem with the GC, this will leak like the
Titanic */
           byte[][] byteArray = new byte[Iterations][];
           byte[] K;
           Console.WriteLine(Iterations.ToString());
           for (int iCount = 0;iCount < Iterations;iCount++)
           {
               K = new byte[4000000];
               byteArray[iCount] = K;
               // fill the array -- just in case the elements must be
assigned in order to leak
               FillByteArray(K);
               // set my reference variable to null
               K = null;

             // this time, don't null the reference to my array.  This will
cause
               // it to stick around in memory.  A leak, but not due to a
GC flaw.
               //byteArray[iCount] = null;

               Console.WriteLine("Iteration: {0:d}",iCount);
           }
       }
       static void FillByteArray(byte[] byteArray)
       {
           for (int iLoopCount = 0;iLoopCount < byteArray.Length;
iLoopCount++)
               byteArray[iLoopCount] = 255;
       }
   }
}

ice

----------------------------------------------------------------------------
----

> If it's a local variable inside the method, setting it to null when you're
> finished with it usually doesn't help.
[quoted text clipped - 54 lines]
> >
> > - Asbj?rn
Jon Skeet [C# MVP] - 18 Feb 2004 12:21 GMT
> can someone explain why this code leaks? this is borrowed code from another
> thread.

What makes you think it's leaking? Running on my box, it was relatively
slow but made it to iteration 121 before I killed it, with Task Manager
showing a working set of only 37M.

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too

Ice - 18 Feb 2004 17:19 GMT
i just needed confirmation (besides my own) that it didn't leak.  i i didn't
see it leak but another poster suggested it did.

thanks.
ice
> > can someone explain why this code leaks? this is borrowed code from another
> > thread.
>
> What makes you think it's leaking? Running on my box, it was relatively
> slow but made it to iteration 121 before I killed it, with Task Manager
> showing a working set of only 37M.
Jon Skeet [C# MVP] - 09 Feb 2004 10:10 GMT
> Yes.  They live on the stack, not the heap so they generate no garbage.

Value types only live on the stack if they're either the values of
local variables or the values of fields in a value type which is on the
stack itself.

See http://www.pobox.com/~skeet/csharp/memory.html

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too

Lord Crc - 10 Feb 2004 23:58 GMT
>For thread-safe pooling, nothing beats the [ThreadStatic] attribute.
>It turns any static field into a ThreadStatic field, so each thread has its
>own copy.

Thanks for the IDisposable example. Reverting to struct's will require
some effort because i've relied on the reference nature of the object
in several places, so i'll have to review all my code carefully. But i
will try it, cause it sounds like it might be worth it.

Thanks for all contributions, i'll be back with more info :)
- Asbjørn
Eric Gunnerson [MS] - 09 Feb 2004 22:44 GMT
Structs may be a valid approach, especially for vectors.

You should definitely download the clr profiler and look at the behavior of
your app:

http://www.microsoft.com/downloads/details.aspx?FamilyId=86CE6052-D7F4-4AEB-9B7A
-94635BEEBDDA&displaylang=en


Signature

Eric Gunnerson

Visit the C# product team at http://www.csharp.net
Eric's blog is at http://weblogs.asp.net/ericgu/

This posting is provided "AS IS" with no warranties, and confers no rights.

> Hi
>
[quoted text clipped - 12 lines]
>
> - Asbj?rn
Frank Hileman - 10 Feb 2004 01:46 GMT
Eric is right, you never want to use classes for things like vectors, unless
you need to box them all the time. I also recommend using structs for 3x2
float matrices, even though they are larger than the recommended size for
structs. Not only will your code be faster, but the value semantic is more
natural, when performing vector/matrix arithmetic, and overloading
operators.

Regards,
Frank Hileman
Prodige Software Corporation

check out VG.net: www.vgdotnet.com
Animated vector graphics system
Integrated VS.net graphics editor

> Hi
>
[quoted text clipped - 12 lines]
>
> - Asbj?rn
Eric Gunnerson [MS] - 11 Feb 2004 00:48 GMT
I'd like to expand a bit on the "when to use structs" question.

The framework design guidelines give a guideline on size, but it's not a
strict guideline. It's mostly a balance between the overhead of having a
heap allocated type versus the cost of passing a value type around. As the
value type gets bigger, the cost of passing it around gets larger, and at
some point this cost will be bigger than the cost of it being a heap
allocated type and passing around the reference.

This point obviously depends upon how much you pass them around, and also
the number of instance you create. It also depends on how you store them -
if you get into boxing, you generally lose any benefit of structs in the
boxing overhead.

I think Frank's advice for 3x2 matrices is on the right track, as they would
only be 24 bytes.

Oh, and you can get value semantics for a reference type merely by making it
an immutable type (ie not methods or properties that modify state). That
will get you value semantics with a reference type, and works fairly well
with operator overloading since it doesn't allow any mutators.

Hope that helps.

Signature

Eric Gunnerson

Visit the C# product team at http://www.csharp.net
Eric's blog is at http://weblogs.asp.net/ericgu/

This posting is provided "AS IS" with no warranties, and confers no rights.

> Eric is right, you never want to use classes for things like vectors, unless
> you need to box them all the time. I also recommend using structs for 3x2
[quoted text clipped - 27 lines]
> >
> > - Asbj?rn
Frank Hileman - 11 Feb 2004 01:16 GMT
Eric,

> Oh, and you can get value semantics for a reference type merely by making it
> an immutable type (ie not methods or properties that modify state). That
> will get you value semantics with a reference type, and works fairly well
> with operator overloading since it doesn't allow any mutators.

But every operator now allocates a new object on the heap. A complex
expression might allocate a bunch of objects, all of which are temporary.
Structs are a natural fit for operator overloading, since memory usage and
locality of reference are optimal. Actually, I am curious, at what size a
float matrix no longer becomes a good candidate for a struct? That is, if
you want operator overloading, complex expressions, etc?

- Frank
Eric Gunnerson [MS] - 11 Feb 2004 20:54 GMT
> Eric,
>
[quoted text clipped - 10 lines]
> float matrix no longer becomes a good candidate for a struct? That is, if
> you want operator overloading, complex expressions, etc?

Yes, that would be true. At some point, the overhead of doing that
allocation will be lower than the overhead of returning the object by value
if it were a value type.

But I agree that in general, structs are more likely to be a good choice,
but if you have a variably-sized object, you can't do it with structs.

As I said in my last post, there's no hard-and-fast rule for when it becomes
better to use a class over a struct. My best advice is to measure the speed
both ways.

Signature

Eric Gunnerson

Visit the C# product team at http://www.csharp.net
Eric's blog is at http://weblogs.asp.net/ericgu/

This posting is provided "AS IS" with no warranties, and confers no rights.

Lord Crc - 11 Feb 2004 21:37 GMT
>Eric is right, you never want to use classes for things like vectors, unless
>you need to box them all the time. I also recommend using structs for 3x2
>float matrices, even though they are larger than the recommended size for
>structs. Not only will your code be faster, but the value semantic is more
>natural, when performing vector/matrix arithmetic, and overloading
>operators.

When porting my vector lib i ran a few tests, and found that even when
not boxing, using classes for vectors and matrices was noticeably
faster. This might be because i use double's and not floats. I will
however try to convert the vectors to structs, and see what happens.
In any case, i've managed to reduce the GC time to a point where it
hovers between 30-40%. I still think there's a lot of room for
improvment though :)

- Asbjørn
Lord Crc - 24 Feb 2004 18:28 GMT
Thanks for all the suggestions. By implementing various ideas from
this thread i've managed to get the GC workload down to a constant
12-15%, which i find much more acceptable.

- Asbjørn

Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.