3

I am sampling a system over the UART port, and then logging the info in a file with a timestamp (including milliseconds). If I sample at 1 second intervals, the data comes back as expected...something like

1:52:45 PM 750 data
1:52:45 PM 750 data
1:52:45 PM 750 data
1:52:46 PM 750 data

However, if I decrease the interval of the timer to 100 ms, the data comes back

1:52:45 PM 531 data
1:52:45 PM 640 data
1:52:45 PM 750 data
1:52:45 PM 859 data
1:52:45 PM 968 data
1:52:46 PM 78 data

Always a little late.

It gets worse with smaller timer intervals... What am I missing here?

jww
  • 97,681
  • 90
  • 411
  • 885
Matt
  • 954
  • 2
  • 9
  • 21
  • You may be experiencing the [Microsoft Minute](http://www.userfriendly.org/cartoons/archives/99mar/19990318.html). – jww Apr 30 '18 at 00:46

4 Answers4

3

Clock drift. Very typical for trivial timers. The reason for this is that they are typically implemented using a sleep function. A sleep function is always guaranteed to sleep for at least the time specified but not guaranteed to not sleep for more time than that, in practice it's always accumulating a drift.

There are ways to write timer that compensate for the drift and on average hit the target.

One of my favorite timers are fixed step ones, that have a discreet tick. It's very simple and looks like this:

var t = DateTime.Now + TimeSpan.FromSeconds(1);
for (;;)
{
    if (DateTime.Now >= t)
    {
        t += TimeSpan.FromSeconds(1); // Tick!
    }
}

That's a crude but effective timer, this following one is an example of a clock I built for a WPF timer where the built in timer was suffering from drifting. This timer is a lot more complex and it does not hog your CPU. But it clearily illustrates typical issues that timers have.

The OnTimerTick here is using a built in timer that suffers from drift but it's adjusting the interval to compensate for the drift.

/// <summary>
/// Occurs when the timer interval has elapsed.
/// </summary>
public event EventHandler Tick;

DispatcherTimer timer;

public bool IsRunning { get { return timer.IsEnabled; } }

long step, nextTick, n;

public TimeSpan Elapsed { get { return new TimeSpan(n * step); } }

public FixedStepDispatcherTimer(TimeSpan interval)
{
    if (interval < TimeSpan.Zero)
    {
        throw new ArgumentOutOfRangeException("interval");
    }
    this.timer = new DispatcherTimer();
    this.timer.Tick += new EventHandler(OnTimerTick);
    this.step = interval.Ticks;
}

TimeSpan GetTimerInterval()
{
    var interval = nextTick - DateTime.Now.Ticks;
    if (interval > 0)
    {
        return new TimeSpan(interval);
    }
    return TimeSpan.Zero; // yield
}

void OnTimerTick(object sender, EventArgs e)
{
    if (DateTime.Now.Ticks >= nextTick)
    {
        n++;
        if (Tick != null)
        {
            Tick(this, EventArgs.Empty);
        }
        nextTick += step;
    }
    var interval = GetTimerInterval();
    Trace.WriteLine(interval);
    timer.Interval = interval;
}

public void Reset()
{
    n = 0;
    nextTick = DateTime.Now.Ticks;
}

public void Start()
{
    var now = DateTime.Now.Ticks;
    nextTick = now + (step - (nextTick % step));
    timer.Interval = GetTimerInterval();
    timer.Start();
}

public void Stop()
{
    timer.Stop();
    nextTick = DateTime.Now.Ticks % step;
}
John Leidegren
  • 59,920
  • 20
  • 131
  • 152
  • Interesting, I come from a background of embedded systems. In those you have finite clock resolution, but the accuracy is as good as the clock frequency. I guess I was assuming these timers were purely counting the processor clock. To a certain extent I guess they are but then you have the .NET overhead to get back into your execution. – Matt Mar 10 '11 at 21:04
  • This stuff holds for native development as well and should not be attributed to any managed code overhead. Anything similar to a loop with a `sleep(1000);` at the end will suffer from the same problem due how the operating system suspend/resume the execution of a thread. This problem has to do with thread handling and remains true for any preemptive multithreaded system where the operating system is switching between concurrent workloads. – John Leidegren Mar 10 '11 at 21:27
0

Try using the Stopwatch class instead. Timer's are not meant to be accurate to that degree.

GendoIkari
  • 11,734
  • 6
  • 62
  • 104
  • Stopwatch and what? how can it control data transfer? – Andrey Mar 10 '11 at 20:34
  • Possibly this is what Andrey was saying, but unless I am mistaken the stopwatch class has no events. – Matt Mar 10 '11 at 21:37
  • Yeah, I took the issue as being that the time reported at each event was incorrect, rather than being that the time that the event actually was executed was incorrect. A stopwatch would be accurate to report when the event happens, but you would still ned an (inaccurate) timer to cause the events to run. – GendoIkari Mar 10 '11 at 21:39
0

The .NET timers don't have infinite resolution. My experiments with .NET 3.5 and .NET 4.0 show that you can expect, at best, 15 ms resolution with the .NET timer objects. My experience has been that with a timer interval of 100 ms, you can expect a tick typically in the 100 to 105 ms range, but I've seen a range as wide as 99 to 120 ms.

For full details see http://www.informit.com/guides/content.aspx?g=dotnet&seqNum=815.

If you need better accuracy, you can use the Windows timers directly. I created a wrapper for the Timer Queue API at http://www.informit.com/guides/content.aspx?g=dotnet&seqNum=817.

Jim Mischel
  • 131,090
  • 20
  • 188
  • 351
0

Windows multi media timers can achieve higher timing resolution. See here for a Windows multi media timer wrapper and example usage https://stackoverflow.com/a/67060100/4856020

datchung
  • 3,778
  • 1
  • 28
  • 29