views:

142

answers:

1

When programming a small OpenGL application using Windows Forms, I discovered something using a profiler that I thought was pretty wonky.

I'm using a System::Windows::Forms::Timer to detect a combination of mouse idling and some other user input. The tick functionality is useful, but on occasion I want to 'reset' the timer. You can't really do this directly, but you get the correct effect by doing this:

this->someTimer->Stop();
this->someTimer->Start();

The timer interval is 50ms. Sometimes my 'reset' code here is run every 30ms. To my surprise, this is a very taxing thing to do - 30% of my program execution is often in these two lines! And it isn't a case of calling these two lines too often. In certain contrived situations this code is called as often as OpenGLSwapBuffers() for example, but no more. I interpret this as meaning that these two lines cost roughly half as much my entire render loop!

Currently I'm using a workaround, but the timer method is a little more elegant. I'm very curious - why are these two lines so computationally expensive? What am I missing? Is there a better way to reset a timer?

+2  A: 

Calling Start() and Stop() sets an internal "enabled" flag, which performs the following work every time it is set (in the case of Start) or unset (in the case of Stop):

public virtual void set_Enabled(bool value)
{
    lock (this.syncObj)
    {
        if (this.enabled != value)
        {
            this.enabled = value;
            if (!base.DesignMode)
            {
                if (value)
                {
                    if (this.timerWindow == null)
                    {
                        this.timerWindow = new TimerNativeWindow(this);
                    }
                    this.timerRoot = GCHandle.Alloc(this);
                    this.timerWindow.StartTimer(this.interval);
                }
                else
                {
                    if (this.timerWindow != null)
                    {
                        this.timerWindow.StopTimer();
                    }
                    if (this.timerRoot.IsAllocated)
                    {
                        this.timerRoot.Free();
                    }
                }
            }
        }
    }
}

As you can see, there is work involved, including handle allocation and management, which goes above and beyond simply setting an internal flag. This may be the expense of the issue you are seeing.

As a possible solution to your issue, I would like to call your attention to the fact that there are three timer implementations in the .NET Framework:

  • System.Timers.Timer
  • System.Threading.Timer
  • System.Windows.Forms.Timer

The System.Timers.Timer and the System.Threading.Timer implementations are lighter-weight implementations of timer functionality, and you should investigate whether one of those lighter implementations will be more performant and still meet the requirements of your project. For the differences in functionality between the implementations, please see this blog post from Mark Michaelis.

JeffFerguson
I stumbled upon the Threading.Timer, but not sure if I ever tried the Timers.Timer. Thank you! I wont have access to the profiler I used for a while, so I'll keep you in the loop re: performance.Indeed, that seems like a decent chunk of code to run! May I ask where you got it from?
Rooke
I used .NET Reflector, available at http://www.red-gate.com/products/reflector/, to take a look at the code inside the System.Windows.Forms.Timer assembly.
JeffFerguson