Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# performance profiler shows long pause, unable to determine what it is from the data provided

I am getting an unexpected spike in my C# application when rendering frames. I have been going over it in a profiler and I noticed the following:

  • When vsync is on, the program seems to relinquish control to the OS (or does something) when swapping the buffers... and somehow stays very consistent in its rendering. This is good because it renders the frames smoothly and without this weird microstutter. The image below is showing what is perceived to be perfectly fluid. This is our case of 'it works perfectly by rendering smoothly without any stuttering'.

VSync image

  • When vsync is off and the program does nothing but writing as fast as it possibly can without any breaks, even though the frames per second is higher, there seems to be these large gaps where nothing is being done. This I am pretty sure is the microstutter I am noticing in game because it will cause the FPS to dramatically drop beneath 60fps. This following image shows something going wrong:

Microstutter

In the above, the delta time is 21ms (which is past the 16.6ms for 60 frames per second) and causes a noticeable stutter in game. It's even worse than this because it only starts rendering after it (as you can see by the solid rectangle after) so this 21ms is a lie, it's more like 40ms according to the graph which is horrible.

This large gap never happens when vsync is on for reasons I do not understand. For those unfamiliar with vsync and games, you can't use vsync in first person shooters because it cripples the input handling due to how it works, so I cannot use vsync and I must investigate the non-vsync version. I need to find why the non-vsync version has these major stalls though.

My question is, how do I know what is causing this extended delay from the image you see above?

The performance profiler says there's a significant amount of waiting time here, upwards of 80% waiting with 20% CPU usage (compared 100% CPU usage when preparing the rendering data).

The profiler also shows that there's no rendering code being run whatsoever in that loop... which is weird because the renderer dominates the performance almost completely, so without any capping of the frames per second it should be inundating the entire graph with a solid blue rectangle.

The problem is that the code the profiler shows is just calls into the polling input and dlls in the area I selected above:

DLL calls

Note that the other call of DispatchRenderFrame is making an OpenGL call, which when I completely remove has no effect on the program, so you can ignore that. This might mean that the user input seen below also has no effect on the microstuttering issue... but I can't remove that since it is part of the library I'm using for window management (OpenTK).

I'm not sure what the CLR Worker thread is or what it is doing. It also happens in the vsync one (desired smooth profiling one) so even though I don't know whether or not it is a culprit here, my guess is it probably is not but I'm not sure because it shows up on the 'desired vsync example' too at the same general location.

Is there some interrupt happening and the OS is taking over but not reinstating my thread because it's classified it as a CPU hog? Just a thought... but then again it shows blue bars in the example so I assume the Main thread isn't actually asleep in what I highlighted and is in fact running?

As you can see, I'm not sure on what the time slice I highlighted is telling me, and is what I need help with. I don't know why the waiting percentage is so high at this point in the code, or where to go from here to diagnose the problem further.

EDIT: I did some rough profiling with the Stopwatch class to see which locations were causing the spikes, and the spikes emanate from only the calculation part. None of the OpenGL invocations cause any lag. It is purely happening inside a function of functions that do math and access data, or write into a pre-allocated array of structs.


Extra notes:

This also has me curious if there is a way so I can force it such that the C# virtual machine gets as much CPU usage as possible?

There is no garbage being generated. This is not a GC issue. I can't afford to have the GC run during the application so I do not generate garbage. Everything in the rendering functions are all structs on the stack, the only time it goes to any heap managed object is a pooled array with more than a large enough size for all the render data. The only time it shows up in the profiler is at the beginning and end, but it does not run during the rendering phase.

like image 606
Water Avatar asked Aug 18 '19 05:08

Water


1 Answers

Is your application window by any chance obscured by the Windows 10 taskbar when the spikes occur? I have seen significant stuttering linked to this phenomenon. I believe the rendering of the semi translucent taskbar implies creation of off-screen buffers which interferes with any frame-critical rendering loop.

like image 137
l33t Avatar answered Oct 12 '22 03:10

l33t