Lifespan of a Collidable Pair?

Discuss any questions about BEPUphysics or problems encountered.
Post Reply
ecosky
Posts: 69
Joined: Fri Nov 04, 2011 7:13 am
Contact:

Lifespan of a Collidable Pair?

Post by ecosky »

Hi Norbo,

I've been doing some experimenting with reducing garbage generation of CollidablePairs. My app has near zero garbage generation, but there is still a little bit that happens as a result of CollidablePair objects. While they are structs, as you know they contain the two Collidable references which in turn make the struct generate some garbage.

I've been able to make some changes that avoid the garbage, but it has one flaw which I have as yet been unable to find a solution for. With my changes, Collidable objects track a index into an array of Collidables. This handle is used by the CollidablePair, removing the only reference types in the CollidablePair and in turn eliminating the garbage. Because the array is fixed in size, Collidables need to register and deregister from this array so the index/handle may be reused.

My early attempts at this involved Colliders permanently reserving their handle and adding a Dispose method to release it. This triggered a cascade of changes related to making a bunch of stuff in Bepu disposable for the sole purpose of ensuring Colliders were disposed properly. This was a lot of work, overhead and complexity that came with increased risk of defects so I eventually abandoned it despite eventually getting it to work. Another option I considered is a finalizer on the Collidable to release the index, but when the game generates no garbage, no finalizers get called and the index pool is eventually exhausted. I could trigger a GC when this occurs to recover the unused handles, but that would defeat the purpose. One possible option is to track a list of WeakReferences to the Colliders and release their index/id when a routine update would check and discover they are unreferenced. I don't really like that idea either, a bit too much management overhead for my taste when I think there is a better solution...

Recently, it crossed my mind that all I really need is for Colliders to be registered when there is at least one CollidablePair that needs to track it. So I modified my handle tracker to have an AddIdRef()/ReleaseIdRef() which were called at what I had hoped would bound the time where a CollidablePair needed to have access to the Collidable objects. So long as all CollidablePair objects correctly added/released references to the Collider, it seems this should make it possible for the Collider to reserve an index on the first AddIdRef (putting the reference to the collider in the array in the process) and release the index (and clear the corresponding reference) on the ReleaseIdRef where the refcounter reached zero.

This all sounds well and good, but my efforts at finding the proper spots to put AddIdRef/ReleaseIdRef have failed, and this mismatch means that it just doesn't work. Yet. I have tried a couple of spots, but I seem to be overlooking something and I've been unable to get the add/release to work as intended. I'd really like to figure this out because it is one of the few remaining sources of garbage generation in my game and while it might not seem like much it definitely adds up and contributes to triggering periodic GC which I would like to elimininate.

I've attached the modified files and a support class in case you want to check it out. The new code is wrapped with #if COLLIDER_HANDLES which I turn on at a project level when working on this.
(Edit: removed it since all this seems to not be necessary based on Norbo's response)

My last attempt has the AddIdRef occurring in GroupPairHandler.TryToAdd, and the ReleaseIdRef in GroupPairHandler.UpdateContacts just before the CollidablePairHandler toReturn is returned to it's Factory. If you have a chance to check this out sometime I would really appreciate it. I am hopeful you will just know where to put the calls to AddIdRef/ReleaseIdRef and it will work once they are in the right place, but perhaps there is some other reason why this can't work. I just don't know; I don't know the internals of Bepu well enough yet to be certain either way. It would be really great to find the answer to this, regardless.

Thanks again for everything Bepu!
Last edited by ecosky on Tue Nov 06, 2012 6:45 pm, edited 1 time in total.
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Lifespan of a Collidable Pair?

Post by Norbo »

While structs containing references do complicate the GC's job (e.g. arrays of such structs must be deeply inspected in reference analysis, as opposed to an array of integers or other value-only structs), creating them does not generate garbage.

If CollidablePairs are appearing in isolation as heap-allocated garbage, there could be boxing going on somewhere. I am not aware of any such places, though- scanning the usages within the library comes up clean, and CLR profiler runs on compound-heavy simulations don't show any CollidablePair garbage.

If you can isolate and reproduce the CollidablePair garbage in a debuggable project, I could take a closer look.
ecosky
Posts: 69
Joined: Fri Nov 04, 2011 7:13 am
Contact:

Re: Lifespan of a Collidable Pair?

Post by ecosky »

Very interesting about structs with references, I didn't know that.

By the way you might want to check out the YourKit profiler, http://yourkit.com/dotnet/purchase/index.jsp It is free for established open source projects and I'm sure Bepu would qualify. It has been very useful to me.

I'm probably just misinterpreting what I'm seeing and wasting a bunch of time in the process. I'll keep looking at this and let you know if I discover anything worth telling you about. Thanks for your response.
ecosky
Posts: 69
Joined: Fri Nov 04, 2011 7:13 am
Contact:

Re: Lifespan of a Collidable Pair?

Post by ecosky »

It's looks like the act of monitoring allocations in the profiler to the demo is enough to cause something to start happening to trigger GCs, despite no new objects being allocated from the heap. I can't explain it but I suspect the profiler is tracking the struct-with-references in a way that triggers the GC while when using struct-with-values doesn't trigger the GC. Very odd! Maybe it's specific to the YourKit profiler.

I modified the Bepu demo (attached) to show a GC counter based on the WeakReference GC-detection technique and I was surprised to see it start incrementing merely by monitoring the memory usage in the profiler. It also ignores the mouse movement if the game isn't active, which helps with switching apps while running the demo.

So, as usual, Bepu is not the problem merely my understanding of it :)
Attachments
DemosGame.cs
(18.95 KiB) Downloaded 231 times
Post Reply