Home Dashboard Directory Help
Search

1 millisecond = 2 milliseconds in .Net Framework 4.5 on Windows 8 by Berney


Status: 

Closed
 as By Design Help for as By Design


1
0
Sign in
to vote
Type: Bug
ID: 772126
Opened: 11/25/2012 4:01:56 AM
Access Restriction: Public
0
Workaround(s)
view
1
User(s) can reproduce this bug

Description

On .Net Framework 2.0, 3.5 and 4.0 on Windows 7, I am able to create an STA thread and run a tight Do While Not ...Loop with a Sleep of 1ms. I have a Stopwatch that counts the iterations. CPU utilization is next to nothing and my stopwatch that debug prints an iteration count each 1 second prints something very close to 1000, which would be the 1000Hz expected when sleeping every 1ms.

This same thread/loop/stopwatch code in .Net Framework 4.5 on Windows 8 gives an output of 500Hz. It's as if 1ms = 2ms on the new framework in Windows 8.

My entire app is based on this loop. Please advise. An answer of "timing critical things aren't supported on Windows" is really the last thing I'm hoping to hear. It's been working well since Windows XP.
Details
Sign in to post a comment.
Posted by Nick Lowe on 5/20/2014 at 4:12 AM
Along with increasing the thread priority, you need to use timeBeginPeriod to resolve this to get a higher timer resolution.

Nick
Posted by Berney on 1/22/2013 at 4:22 PM
I would really like to encourage you not to close this out and accept poor performance as "By Design". It's more than possible to have the .Net Framework guys utilize the correct Win32 API's and offer the same performance that Windows 7 offered. Many of us, gamers or industrial automation folks, etc.. need the .Net framework to provide as accurate a timing as possible. When I later learned that this was all done intentionally to improve battery life for tablets and no way to disable it was provided, I was with somewhat distraught with the path we're on. Again, I would highly encourage you not to be accepting of it. Better performance is possible and well within reach.
Posted by Berney on 1/22/2013 at 4:10 PM
I'm sorry, I meant to say "sleeps for 1 millisecond" below. Yesl, I will dig up the code and post it here. It essentially uses MultimediaTimer as opposed to any of the timers in .Net. The engineer wrote a nice little test app that shows the poor performance of all the .Net timers with and withour added stress, then compared it to the Multimedia timer. The conclusion was that the multimedia timer is the only way to get back the same performance I used to have in Windows 7 without a workaround.
Posted by Microsoft on 1/22/2013 at 4:05 PM
Would you mind posting the p/invoke-based workaround here? It may be useful to anyone else who finds themselves in the same situation.
Posted by Berney on 1/22/2013 at 3:53 PM
There is nothing to it. Windows 7 significantly outperformed Windows 8. I can a little loop that writes to USB or a serial port, then sleeps for 1 second. I can do the same firing by timer. I can and put an oscilloscope on the serial line and see that Windows 7 is accurate to the millisecond and output a 1000Hz stream of data at 1ms intervals. Windows 8 will output a 500Hz stream and then randomly throttle that back all on its own to 60Hz. To be blunt, from a timer perspective, this is the worst OS since Vista.

You don't need an oscilloscope to measure this. You can run a 1 second stopwatch and then run a timer that fires each 1ms. Inside the timer fire, just increment a variable that increases the count. On Windows 7, each time that your 1 second stopwatch expires, your counter will be 1000 which indicates that the timer fired each 1ms as it should. On Windows 8 again, it will be 500 and occasionally and randomly 60 with no workload on the OC. This essentially proves the far better timing on Windows 7.

I wound up opening a case on this issue. Microsoft has provided me with a workaround using pinvoke. I'm satisfied with the solution.

Posted by Microsoft on 1/22/2013 at 3:04 PM
Thank you for the feedback. As you've observed, Windows by default does not use a very high resolution timer for most operations, such as Sleep. I believe the current default resolution in Windows 8 is 15.6ms, which seems to match some of your observations.

Previous Windows versions have used different default timer resolutions, but as far as I know there has never been an NT-based Windows version that could measure Sleep time down to 1 millisecond.

This makes me wonder how you are seeing the results you see on previous Windows versions. It is possible that you have some application running (perhaps in the background) on those machines that alters the timer resolution? I believe some of the multimedia timer APIs are capable of this. It's also possible that drivers might alter the timer resultion, though that's pure speculation on my part.

In any case, the solution is to use a more precise timing mechanism. Sleep was not designed to be this accurate. (BTW, the CLR's Timer object is built around calls to Sleep on a hidden ThreadPool thread, so it will not give you any better resolution.)

Good luck. If you do find how you are getting such high resolution on those other machines, please post it here. I am very curious, and will be watching this page.

Eric Eilebrecht
SDE, Common Language Runtime.
Posted by Berney on 12/6/2012 at 1:12 AM
I've looked for alternatives to achieve proper timing in Windows 8 to no avail. Even the .Net Timer set to call a function at 1 ms intervals actually calls it at 16ms intervals. I've checked all of my power settings to see if some kind of throttling may be biting me but no luck there either. I have also verified that the same code executes properly on XP and Windows 7.

Is there any word on this? Will it be resolved in a service pack perhaps?

I am presently faced with either finding a solution or suggesting to our customers that they treat Windows 8 like Vista and avoid it.
Posted by Berney on 11/26/2012 at 4:27 PM
It's verified. I can take a tester app to different operating systems on the same dual boot machine. High resolution timing is correct everywhere but on Windows 8.
Posted by Berney on 11/26/2012 at 2:33 PM
The StopWatch has the exact same issue and gives the exact same output counts.

To test this, on Win 8 / .Net Framework 4.5, set a timer to run at 1ms and have it can all function that simply increments a count varibale. It will not reach a count of 1000, not even cose. For years now, Windows 7 and XP could do this with no issue. In Windows 8 you will get a result of just over 500 counts when it should be 1000. What's more interesting, is that it will randomly (when nothing else is running) decide to give counts of 65 when it should be 1000 and it all seems to scale up. Setting a timer to 16ms will give results along the lines of a 32ms timer.
Posted by Sergey Sakharov on 11/26/2012 at 4:36 AM
If you app depends on precise timings use High Resolution Timers, there are exist Win32 API functions and also StopWatch .NET class which utilize these API.
Using Sleep is bad idea here. Run additional CPU intensive application(s) and you will get even more random result for your application.
Posted by Berney on 11/26/2012 at 3:27 AM
Most of the time, the issue is scaling predictably. For example, a 16ms sleep seems to be about a 32ms sleep. I am testing on a Core i7 / Win 8 / 16gb RAM. All previous operating systems except for Vista have no timing issues on this box so I think it's safe to assume it's not a hardware matter.
Posted by Microsoft on 11/25/2012 at 11:52 PM
Thank you for submitting feedback on Visual Studio and .NET Framework. Your issue has been routed to the appropriate VS development team for investigation. We will contact you if we require any additional information.
Posted by Berney on 11/25/2012 at 11:30 PM
Setting Process Priority to real time ( System.Diagnostics.Process.GetCurrentProcess.PriorityClass = ProcessPriorityClass.RealTime) has no impact on this issue. I hope there is some magic setting to get the correct behavior.
Posted by Berney on 11/25/2012 at 11:15 PM
I'm afraid it gets even worse. On random occasions, when I run that same code the result will be around 65 when it should be 1000. Switching back and forth between .Net Framework 4 and 4.5 sometimes fixes it.

I also tried await Task.Delay(1) in this loop and it does the same thing.

I am mystified about timing in Windows 8 at this point.
Posted by Berney on 11/25/2012 at 10:52 PM
I've taken this testing a bit further. I'm afraid that it is a Windows 8 problem!

If you create a vanilla WPF app from the template in VS 2012 and then past in the code I've included below you'll see some strange behavior no matter what framework you target in VS 2012 on Windows 8. I tried 3.5, 4.0 and 4.5 and got the same poor result in each.

This code below will simply print the result of the loop with 1ms break that works well in XP and Windows 7 on the same machine. The result should be 1000 samples per second, or give or take maybe 50 samples since it's not a real time OS. What you will see is that it is almost always 500'ish but on occasion will jump to 1000 as it should be.

Here 's the code to paste into the MainWindow.xam.vb of a VB.Net WPF project.

Imports System.Timers

Class MainWindow
    Private tThreadTest As System.Threading.Thread
    Private swPerSec As New Timers.Timer(1000)
    Private intSamplesPerSec = 0

    Public Sub New()

        ' This call is required by the designer.
        InitializeComponent()

        'Setup PerSec Timer (Used by Polling or Event Branches)
        RemoveHandler swPerSec.Elapsed, AddressOf PerSecTimerHandler
        AddHandler swPerSec.Elapsed, AddressOf PerSecTimerHandler
        swPerSec.Start()

        'Set GC Mode
        'System.Runtime.GCSettings.LatencyMode = Runtime.GCLatencyMode.SustainedLowLatency

        'Start a thread with tight loop
        tThreadTest = New System.Threading.Thread(AddressOf TestLoop)
        tThreadTest.SetApartmentState(System.Threading.ApartmentState.STA)
        tThreadTest.Name = "Test Thread"
        tThreadTest.Priority = System.Threading.ThreadPriority.Highest
        tThreadTest.Start()

    End Sub

    'Handle Timer (1sec) Event
    Private Sub PerSecTimerHandler(ByVal source As Object, ByVal e As ElapsedEventArgs)
        'Output Perf Stats
        Debug.Print("SAMPLES PER SEC=" & intSamplesPerSec)
        intSamplesPerSec = 0
    End Sub

    Private Sub TestLoop()
        Do While Not tThreadTest.ThreadState = System.Threading.ThreadState.Aborted And Not tThreadTest.ThreadState = System.Threading.ThreadState.AbortRequested
            intSamplesPerSec += 1
            System.Threading.Thread.Sleep(1)
        Loop

    End Sub
End Class
Posted by Microsoft on 11/25/2012 at 4:53 AM
Thank you for your feedback, we are currently reviewing the issue you have submitted. If this issue is urgent, please contact support directly(http://support.microsoft.com)
Sign in to post a workaround.
File Name Submitted By Submitted On File Size  
TimerTest_OneShot.zip (restricted) 1/22/2013 -