Are Timers and Loops in .Net accurate?

后端 未结 4 496
旧巷少年郎
旧巷少年郎 2020-11-29 09:51

While developing a program to calculate the frequency and pulse width of the pulse generated by a 555 timer IC, coming to the PC via PC Parallel port. I noticed that every t

相关标签:
4条回答
  • 2020-11-29 10:23

    A careful implementation allows the measurement of time periods on most windows platforms down to an accuracy of just a few microseconds. Note the following facts:

    1. Windows is not a real-time OS: This does not matter here!

    2. Make use of process/thread priorities: SetPriorityClass at up to REALTIME_PRIORITY_CLASS and SetThreadPriority at up to THREAD_PRIORITY_TIME_CRITICAL. Make sure to have safe code, because these priorities can lock the system while the calling thread is busy. (Process.PriorityClass and Thread.Priority are not quite capable to raise priorities to the required levels.)

    3. Possibly increase the systems interrupt period and time update interval by means of Multimedia Timer. (There are various .NET projects that wrap these multimedia timer functions.)

    4. On multicore systems the choice of the core can also influence the accuracy. It is advantagous to force the thread waiting for a timer event to wait on Processor0. The SetThreadAffinityMask function allows to bind a thread to a specific cpu. (For .Net applications see Thread.ProcessorAffinity.)

    5. Use QueryPerformanceCounter and QueryPerformanceFrequency as rescources for high frequency time measures. (For .Net applications see: Creating a QueryPerfCounter Wrapper Class)

    6. Make sure that the value of the performance counter frequency is calibrated. The value returned by QueryPerformanceFrequency deviates from the observed value by an offset and by some thermal drift. This may/will introduce errors of many us/s. See the Windows Timestamp Project to find out how such a calibration can be done.

    And: Yes, you may have to go for some hard coding. But microsecond timing can be observed very reliable on windows platforms.

    Note: Windows is not a real-time operating system. But the timers available on windows are very precise. They do exactly what they are supposed to do and the do it at very high accuracy. The fact that there are lots of complaints about windows timers and their accuracy is that their behavior depends a lot on the underlying hardware. That is also the reason why the documentation has many flaws. It is strongly recommended to diagnose the hardware in order to find the individual time service capabilities. Unfortunately this causes any program to have some extra lines of code to be platform independent.

    0 讨论(0)
  • 2020-11-29 10:23

    Ed's answer is correct: timers on Windows are not accurate. Well, certainly not accurate enough to measure hardware generated signals.

    But, if I had to measure frequency and pulse width, I'd take a hundred samples or so and average them. Perhaps like so:

    private StopWatch _Sw = new StopWatch();
    private List<TimeSpan> _Samples = new List<TimeSpan>();
    private Timer _Timer = new Timer(TimerTick, TimeSpan.FromSeconds(1));
    private const int RequiredSamples = 100;
    
    private void StartSampling()
    {
        // You can change this next line to PriorityClass.RealTime if you're careful.
        System.Diagnostics.Process.GetCurrentProcess().BasePriority 
                                  = PriorityClass.High;
        _Samples.Capacity = RequiredSamples;
        _Timer.Start();
        _Sw.Start();
        Hook555Timer(On555Pulse);
    }
    
    private void On555Pulse(object sender, EventArgs e)
    {
        _Sample.Add(_Sw.Elapsed);
    }
    
    private void TimerTick(object sender, EventArgs e)
    {
        if (_Samples.Count > RequiredSamples)
        {
            System.Diagnostics.Process.GetCurrentProcess().BasePriority 
                                        = PriorityClass.Normal;
            _Timer.Stop();
            _Sw.Stop();
            UnHook555Timer(On555Pulse);
    
            // You can now use the time between each TimeSpan 
            // in _Samples to determine statistics about your timer.
            // Eg: Min / Max duration, average and median duration.
            var durations = _Samples
                    .Zip(_Samples.Skip(1), 
                        (a,b) => new { First = a, Second = b } )
                    .Select(pair => pair.Second.Subtract(pair.First));
            var minTime = durations.Min(ts => ts.TotalMilliseconds);
            var maxTime = durations.Max(ts => ts.TotalMilliseconds);
            var averageTime = durations.Average(ts => ts.TotalMilliseconds);
            // I don't think LINQ has a Median() aggregate out of the box.
            // Some comment about "an exercise for the reader" goes here.
            var medianTime = durations.Median(ts => ts.TotalMilliseconds);
    
            var frequency = _Samples.Last()
                                     .Subtract(_Samples.First())
                                          .TotalSeconds / _Samples.Count;
        }
    }
    

    (NB: code written in notepad, not likely to work without further modifications)

    My precision is now determined by StopWatch rather than the Timer (note that Stopwatch may not be any more precise than a Timer on your system, check the Frequency and IsHighResolution properties). And I'm increasing the priority of the process during sampling to minimise other processes pre-empting my process.

    But even so, because Windows is not a RTOS, the best you can do is take lots of samples and use some statistics to get an approximation of the answer.

    0 讨论(0)
  • 2020-11-29 10:29

    1) Don't use DateTime.Now for performance measurements, use StopWatch.

    2) "OUTPUT: Should be same, but .."

    Why should they? You are running managed/JIT'ed code on a non RTOS (real time operating system). You're code can get bounced at any time if the OS feels like it. What lead you to believe that running the same code N times in this environment should always produce the same results to such a small degree?

    3) Timers on Windows have a ~15ms resolution. Your best bet for very accurate timings is the HighPerformanceTimer API on systems (CPU's) which support it. You haven't even shown us the timer's interval.

    You're failing to consider many variables here and you're basing your predictions upon faulty assumptions. How many times are you even measuring this code? Are you taking into account the time needed to compile it the first time through? Are you running in release mode? Through VS? Are there many tasks running in the background? I could go on.

    0 讨论(0)
  • 2020-11-29 10:45

    First of all, you don't know how far into the current second the time is when you button2_click is called. So, it's basically random how much of a second is left when the MessageBox is displayed--which is what you're seeing.

    Second, how many CPU cycles a loop gets within a span of time has nothing to do with accuracy.

    How many cycles any given thread gets depends on what else is going on in the system. If the system decided that a whole bunch of cycles needed to go to another process, your thread would be "starved" for some time.

    Maybe you can detail what you're really trying to do, and someone can offer some advice.

    0 讨论(0)
提交回复
热议问题