Water
Water

Reputation: 3655

Any .NET Core 3.0 project keeps spawning a thread that damages my runtime performance

I am writing a 3D renderer using OpenTK on .NET Core 3.0. I was running into an issue where my game would have these small 'microstutter' pauses, whereby I'd get this very small jerky motion that would last probably 50-100 milliseconds. It is quite annoying and happens every 10-20 seconds.

I found three problems that were causing them and eliminated two of them. The final one I have no idea how to solve.

After watching the output window in debug mode, I noticed the microstutters seem to almost always coincide with the following being logged:

The thread 0x4f1e has exited with code 0 (0x0).

where the thread ID will vary.

My goal is to get rid of whatever is spawning these... but they seem to be happening even in a Console App that just loops forever as follows

using System;

namespace ThreadStutterTestApp
{
    class Program
    {
        static void Main(string[] args)
        {
            int i = 0;
            while (i <= 0)
            {
                i = -1;
            }
        }
    }
}

The microstutters happen in both debug and release mode, and they also happen whether I'm running the application inside VS (in this case VS2019, using .NET Core 3.0 on whatever the default C# version is for .NET Core 3.0) or from the command line outside of it.

I seem to get the same frequency of microstuttering in debug or release, so I assume whatever is generating this thread appears in both. The problem is that I don't get the "Thread ... exited" message in release mode so I could never tell what was causing it... it was only today that I made the connection of it happening when I was checking it out in debug mode.

It is also not any invocations of writing to the output in VS because when I invoke Debug.WriteLine("Test"); there is no stutters at all that result from it in game. This appears to exclusively be linked to the threads terminating.

This thread is not there at the application start when viewing the Threads window in the debugger, it'll usually appear after 10-15 seconds after the start of the application (and then as said previously, will eventually do whatever it is doing and then die, and mess with my engine performance).

The thread that spawns and dies does not appear on .NET Core 2.0 or 2.1 at all. It does however appear on .NET Core 2.2, but it appears once and dies once and that's it. On .NET Core 3.0 (using C# 8, but the C# version may or may not have anything to do with it, unlikely... but unsure at this point) it keeps dying and coming back. In the console application there's one rogue thread but in the OpenTK one it looks like there's two threads that do this (which I don't think it's OpenTK but rather something is spawning a second one for reasons I don't understand).

Also my NuGet installations are empty for the barebones console application, so I it's not some rogue library giving me grief for that one, and whatever is spawning this troublesome thread is doing it even on a basic console app.

Every single time this mystery thread exits, my application will get 100% guaranteed stutter that occurs exactly as the thread exits. It is perfectly reproducible.

How do I go about solving this? What is spawning this thread? Can I stop it? Is this a bug I should report (if so, where)?

Edit: Interestingly, there appear to be ~2 stutters at the beginning of the application that do not coincide with the thread exiting, but after those two then it will always forever onwards coincide with threads dying. For example:

00 sec - Application start

05 sec - microstutter, no thread exiting written to the debug window

20 sec - microstutter, no thread exiting written to the debug window

40 sec - microstutter, thread exiting

65 sec - microstutter, thread exiting

83 sec - microstutter, thread exiting

99 sec - microstutter, thread exiting

... continues whereby all microstutters align with a thread exiting

I am able to view both the fullscreen window and the debug in real time due to having two monitors.

Upvotes: 1

Views: 208

Answers (1)

Sean Werkema
Sean Werkema

Reputation: 6125

I've been running into this too, in a game I've been working on in MonoGame; I had the same exact "mysterious thread exits" that seemed to be caused by nothing predictable, and that would cause the gameplay to "pop" and "stutter" every few seconds. Sometimes it would be affected by input, and sometimes it would happen on its own, but it always was paired with the "mysterious thread exits." I was pulling out my hair, and I was considering things as drastic as switching game frameworks, or even porting all my code from C# to C++.

But after quite a lot of research, it appears to be a bug in the performance-counting logic in .NET Core 2.2/3.x that's used in the JIT's Tiered Compilation system. The background threads seem to be the JIT kicking in to take previously-JITted code and optimize it further because it's being detected as in heavy use. The .NET team says this will be fixed in .NET 5; but for now, it seems they've suggested turning off tiered compilation entirely, which you can do in the .csproj file, among other places:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TieredCompilation>false</TieredCompilation>
  </PropertyGroup>
</Project>

The down side, of course, is that the JIT is less capable, and may generate less-optimized code; but in my case, it got rid of the unexpected gameplay stutters.

Upvotes: 1

Related Questions