C# Wrapper Garbage Collector performance

I’ve noticed that when running NuiTrack with the C# wrapper, the GC is running almost continuosly; see VS memory diagnostic window below.

This happens even with all the most basic examples.

Tracking the memory objects being created and disposed, I noticed that new DepthFrame, ColorFrame, UserFrame objects are created, including their internal Byte[] Data arrays. but they’re not reused in the next frames.

This may be convenient for naive, simple or short lived applications but for applications that need to run for HOURS, this is a very inefficient way of handling memory, since the GC is running all the time.

consequences of not reusing the byte buffers on new frames:

  • It forces the GC to run all the time, increasing the possibility of triggering a generation 2 GC, which blocks the application for a few seconds; very bad for real time applications. Even if the GC only runs a generation 1 recollection, running the GC all the time consumes CPU.

  • Huge buffers, like those used for allocating images, are allocated in the Large Object Heap, not in the normal heap, this is potentially dangerous because LOH objects are not automatically defragmented by the GC, which can result in an unexpected Out Of Memory exception when trying to allocate a new buffer, even if the system reports there’s enough memory.

  • It increases the memory requirements pressure. All previously allocated buffers stay in memory until a new GC runs. The only way to reduce memory pressure is to manually run GC after reading every frame. This would be incredibly inefficient.

As an example, here’s a screenshot of visual Studio Memory diagnostic, running NuiTrack vs running KinectSDK:

KinectSDK:

NuiTrack:

as you can see, NuiTrack consumes a lot more memory (due to not releasing/reusing previously allocated frames), and triggers GC recollections a lot more often.

3 Likes

Hi Vpenades,

Please advise what sensor do you use?
We can provide you with a special version of Nuitrack that allows to avoid the issues that you’ve mentioned, but in this case the frame size (width and height) will be fixed. If it suits you, please contact support-nuitrack@3divi.com, so we can send it to you.

Any updates on this? It makes the whole SDK basically unusable in larger projects as GC Gen2 is triggered every other second due to huge allocations. The wrapper should do some kind of pooling internally (ArrayPool for example).

I’m surprised too that the fix we received months ago has not been ported yet to the main SDK.

@olga.kuzminykh I suggested this in the past. If the c# wrapper is not being maitained, you should at least consider publishing the source code of the C# wrapper to github (NOT the native libraries which are proprietary of nuitrack) So people will be able to improve it with pull requests.

Not only the garbage collection needs to be fixed in the main SDK, a low level Lock-IntPtr-Unlock mechanism would be desirable to avoid redundant buffer copies for those that can take advantage of it.

Okey, this issue has been resolved on Nuitrack Version .31 by exposing a direct IntPtr access.

As I commented at Github, for those requiring a plain c# array, the IntPtr data can be easily copied to an array with a Span:

var span = new Span<Byte>(frame.Data.ToPointer(), frame.DataSize);
var array = span.ToArray();