Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Windows 11 reduce the performance when run without vs debugger #10326

Open
cqgis opened this issue Jan 23, 2025 · 2 comments
Open

Windows 11 reduce the performance when run without vs debugger #10326

cqgis opened this issue Jan 23, 2025 · 2 comments
Labels
Investigate Requires further investigation by the WPF team.

Comments

@cqgis
Copy link

cqgis commented Jan 23, 2025

I created a WPF Application (.net 8) with a TextBlock to show the FPS of the application.
I used two tasks to calculate the FPS and update the TextBlock.

 private volatile int                  _frameCount    = 0;
 private readonly ManualResetEventSlim _quantumWaiter = new(false);

 private void MainWindow_Loaded(object sender, RoutedEventArgs e)
 {
     Task.Factory.StartNew(() =>
     {
         var sw    = new Stopwatch();
         var delay = TimeSpan.FromMilliseconds(5);
         while (true)
         {
             sw.Restart();
             Interlocked.Add(ref this._frameCount, 1);
             sw.Stop();
             var waitting = delay - sw.Elapsed;
             this._quantumWaiter.Wait(waitting);
         }
     });

     Task.Factory.StartNew(async () =>
     {
         DateTime? lastTime = null;
         while (true)
         {
             var datetime = DateTime.Now;
             var count    = Interlocked.Exchange(ref this._frameCount, 0);
             if (lastTime != null)
             {
                 var elapsed = datetime - lastTime.Value;
                 var fps     = count / elapsed.TotalSeconds;
                 await this.Dispatcher.InvokeAsync(() => { this._txt.Text = $"FPS: {fps:0.0}"; });
             }

             lastTime = datetime;
             await Task.Delay(TimeSpan.FromSeconds(1));
         }
     });
 }

The problem is that when run the application on windows 11, something strange happens.
Expand on the problem:

  • On Windows 11, run the application with visualsutdio debugger, the FPS is around 200.
  • On Windows 11, run the application without vs debugger, the FPS is around 60. not only debug version but also release version.
  • On Windows 11, run the application without vs debugger, the FPS is around 60. the attached process to vs debugger, the FPS is around 200.
  • On Windows 10, the FPS is always around 200, not matter if run with or without vs debugger.

Not only WPF application but also other types of appliation.

I can't understand why the FPS is different on Windows 11 when run with or without the vs debugger. some one can help me?

Image Image
@cqgis cqgis changed the title Some strange behavior on Windows 11 reduce the performance of the application when run without the vs debugger Windows 11 reduce the performance when run without vs debugger Jan 23, 2025
@himgoyalmicro himgoyalmicro added the Investigate Requires further investigation by the WPF team. label Jan 24, 2025
@MichaeIDietrich
Copy link
Contributor

I would say this issue is not really related to WPF, but generally to Windows development.
As far as I know, there is something called timer resolution in Windows, which more or less defines the precision of timers.
By default it's around 15.6ms which is equal to the 64 FPS you can see in your test.

Pretty much every timer is affected by this: Task.Delay, Thread.Sleep, ManualResetEvent.Wait, etc.
If you would replace _quantumWaiter.Wait with manually waiting using a while loop until 5ms are over, you would see that you will be back to ~200 FPS.

The resolution of timers is limited to reduce energy consumption.
But there is an option to opt-in into high precision timers and I guess the Visual Studio debugger does exactly this out-of-the-box to simplify debugging performance related stuff without manually caring about this topic.

You can manually enable high precision timers with (undocumented) NtSetTimerResolution from ntdll.dll:

[DllImport("ntdll.dll")]
private static extern int NtSetTimerResolution(int DesiredResolution, bool SetResolution, out int CurrentResolution);

NtSetTimerResolution(10000, true, out _);

There also seems to be the option in Windows 10 and above to explicitly create a high precision timer if needed using CreateWaitableTimerExW with CREATE_WAITABLE_TIMER_HIGH_RESOLUTION flag.

@h3xds1nz
Copy link
Contributor

h3xds1nz commented Jan 24, 2025

The resolution of timers is limited to reduce energy consumption. But there is an option to opt-in into high precision timers and I guess the Visual Studio debugger does exactly this out-of-the-box to simplify debugging performance related stuff without manually caring about this topic.

It is actually because of backcompat, it's been 15.625 ms since first the Windows NT where it wasn't reasonable to have a better resolution, previously on 16bit Windows it used to be 55 ms; strictly due to interrupts needed back in the dark days. These days (on NT) it is exposed via KUSER_SHARED_DATA which is where GetTickCount and friends get it.

Visual Studio does indeed call NtSetTimerResolution and sets the resolution to 10000 (to be more precise, it queries the maximum resolution and multiplies it by two) from a background thread (at least on Windows 10), only upon startup though. So it would be best I guess to check the resolution on your Win11 machine (see below). It is possible some other app is resetting the resolution.

You can manually enable high precision timers with (undocumented) NtSetTimerResolution from ntdll.dll:

[DllImport("ntdll.dll")]
private static extern int NtSetTimerResolution(int DesiredResolution, bool SetResolution, out int CurrentResolution);

You should also note that this is a system-wide setting. You may use NtQueryTimerResolution to query the periods for the system to see whether they differ (and how much) on your different windows installations/machines during runtime.

Signature for completion (they should be uint even in NtSetTimerResolution btw):

public static extern NTSTATUS NtQueryTimerResolution(out uint MinimumResolution, out uint MaximumResolution, out uint CurrentResolution);

Do note that DateTime.Now is actually tied to this resolution as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Investigate Requires further investigation by the WPF team.
Projects
None yet
Development

No branches or pull requests

4 participants