2

I'm writing trading software and need to QoS one method that should not be executed more often than 10 times per second. As I'm begginer in C# and almost not familar with libraries I would like to double-check if my code is "optimal". I'm using Stopwatch because I don't know any other timers in C#.

    Stopwatch updateStopwatch = Stopwatch.StartNew();

    private void update()
    {
        if (updateStopwatch.ElapsedMilliseconds < 100)
        {
            Console.WriteLine("!skip update " + updateStopwatch.ElapsedMilliseconds);
            return;
        } else
        {
            Console.WriteLine("!update");
            updateStopwatch.Restart();;
        }
        // do work here
    }

upd Now it seems that Stopwatch is pretty good for this task. However probably it would be too slow, if so probably DateTime would be better. sell also Stopwatch vs. using System.DateTime.Now for timing events

Community
  • 1
  • 1
Oleg Vazhnev
  • 23,239
  • 54
  • 171
  • 305

4 Answers4

3

Your technique of using Stopwatch is the best solution to prevent the code from executing more frequently. As others have said, using a Timer is a better solution if you want to make sure that the method is executed on a schedule.

Any approach based on DateTime is fundamentally broken because it will fail when the date changes. This is especially noticeable during the Daylight Saving Time switches. When we "spring ahead", there's the potential of the update running twice in quick succession because the code thinks that it's been an hour since the previous update. That's not too bad. But when we "fall back", the update will be suspended for a full hour because the last update time is set an hour ahead.

The same kind of thing can happen, although not as severely, if your computer is set to update its time periodically from an NTP server. If the time is set ahead, then there is the potential for two updates to happen in quick succession. If the time is set back, there's the potential for updates not to happen for the amount of time the clock was set back.

There are ways around the problem (such as using the absolute value of the number of milliseconds), but then you're just putting a bandage on a broken solution. You shouldn't depend on DateTime for intervals like this because your program isn't in control of the system clock--it can change at any time.

Stopwatch is the only reasonable solution here because it depends on the CPU's performance counter, which only increases. You don't have the problems of somebody setting the counter back, and you don't have the rollover problems you would encounter with something like Environment.TickCount.

There's some idea that Stopwatch incurs a performance penalty that DateTime doesn't. My testing shows that to be untrue.

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

Stopwatches and timers are fairly expensive objects to use. You could simply hold a DateTime object as a variable and perform a comparison.

DateTime lastCheck = DateTime.Now;

private void update()
{
    // DateTime.Subtract returns a TimeSpan
    int elapsed = DateTime.Now.Subtract(lastCheck).Milliseconds;
    if (elapsed < 100)
    {
        Console.WriteLine("!skip update " + elapsed.ToString());
        return;
    } else
    {
        Console.WriteLine("!update");
        lastCheck = DateTime.Now;
    }
    // do work here
}
Joel Etherton
  • 37,325
  • 10
  • 89
  • 104
  • DateTime is also fairly expensive also. The only difference would be your using the main thread instead of child of the main thread to handle it. – Security Hound Apr 15 '11 at 14:54
  • @Ramhound: DateTime is not nearly as expensive as Timers or StopWatches. There are a whole host of events that have to be loaded and fired for those that DateTime does not require. Functionally DateTime is more expensive than ideal probably, but still maybe slightly easier to maintain and simpler to implement. In any case, this is just an idea and quite possibly not the most ideal solution. – Joel Etherton Apr 15 '11 at 15:07
  • How is `Stopwatch` expensive? All it does is keep track of the start time, and every time you access the `Elapsed` property it calls `QueryPerformanceCounter`. There's no "event" involved at all. – Jim Mischel Apr 15 '11 at 17:02
  • You'll want `TotalMilliseconds` there, rather than `Milliseconds`. – Jim Mischel Apr 15 '11 at 17:25
  • @Jim Mischel: Right TotalMilliseconds would be more appropriate. I don't have benchmarks in front of me. I read an article (that I conveniently can't find now) that indicated a performance hit for Timers and StopWatches (Timers being the worst). I can't speak authoritatively, so take that with a grain of salt. – Joel Etherton Apr 15 '11 at 17:35
1

I would not use a Stopwatch or anything other Timer-like. Instead just store the time of the method call and only execute the subsequent calls if the difference between the current and the stored time is bigger than 100ms.

You could implement a helper class to do this in a more general way:

public class TimedGate
{
     private DateTime m_Last;
     private TimeSpan m_Gap;

     public TimedGate(TimeSpan gap)
     {
         m_Gap = gap;
     }

     public bool TryEnter()
     {            
         DateTime now = DateTime.UtcNow;
         if (now.Subtract(m_Last) > m_Gap)
         {
              m_LastEntered = now;   
              return true;                 
         }
         return false;  
     }
}

Use it like this:

TimedGate m_UpdateGate = new TimedGate(TimeSpan.FromMilliseconds(100));

private void Update()
{
    if (m_UpdateGate.TryEnter())
    {
        Console.WriteLine("!update");

        // do work here
    }
    else
    {
        Console.WriteLine("!skip update");
    }
}
g t
  • 7,287
  • 7
  • 50
  • 85
Florian Greinacher
  • 14,478
  • 1
  • 35
  • 53
0

There is always the System.Timer timer.

That is probably easier to work with than the Stopwatch (which normally is used to measure how long time things take).

Code:

    var timer = new System.Timers.Timer();

    // Hook up the Elapsed event for the timer using a lambda
    timer.Elapsed += (o, e) => Console.WriteLine("Timer elapsed");

    // Set the Interval to 100 ms
    timer.Interval = 100;

    // Start the timer.
    timer.Enabled = true;

MSDN docs: http://msdn.microsoft.com/en-us/library/system.timers.timer(v=VS.100).aspx

Mikael Östberg
  • 16,982
  • 6
  • 61
  • 79
  • StopWatch is based on a high resolution timer. I don't think System.Timer is based on a high resolution timer. StopWatch can be checked with `IsHighResolution`. –  Apr 15 '11 at 14:36
  • Either way, the Stopwatch is the to *measure* time and not to be used as a timer. And: "If the installed hardware and operating system support a high-resolution performance counter, then the Stopwatch class uses that counter to measure elapsed time. Otherwise, the Stopwatch class uses the system timer to measure elapsed time." – Mikael Östberg Apr 15 '11 at 14:41
  • Well actually I don't need timer meaning that I don't need to generate events every 100 ms. I need to prevent from generating events more frequently than one per 100 ms. – Oleg Vazhnev Apr 15 '11 at 19:17