none
C# System.Timers.Timer. Is there really that much overhead in a winform app vs. console? Example Code RRS feed

  • General discussion

  • Hi all…

    I wanted to create a timer that could app where I could monitor how many times a timer will tick per second.  This all came about because I wanted to be able to throttle down how many times I was sending things over a network stream and I wanted to find the sweet spot in milliseconds and using a timer. Think FPS. 

     

    To do this I used two System.Timers.Timer objects. 
    _tpsTimer (ticks per second)

    • Ticks on a user input interval so I can test to see how often I’d want to trigger based on FPS.
    • Simply increments a counter variable “_tpsCount” tracking how many times its delegate was fired off

    _oneSecondTimer

    • Hard coded to 1000 milliseconds, 1 second.
    • Displays the “_tpsCount” variable showing me how many times the _tpsTimer fired its event in one second.

      

    I wrote a couple versions of the app. The logic behind these two apps are identical except for the fact that one is a winform one is a console.  The two versions I’m most interested in posting about are

    • SystemTimers – A Windows form app using System.Timers.Timer
    • SystemTimersConsole – A console app using System.Timers.Timer

    I was surprised at how much of a difference there was in ticks per second between a console app and windows form app.  Is there REALLY that much overhead in a windows form app?  Secondly, am I way off base in using timers for this purpose? Ultimately wanting to create a FPS throttle... How many images I send over a network per second.

    The following image shows various tests at different millisecond intervals.  The columns are :

    • Milli Sec column : Millisecond interval.
    • Console : At the given milli sec interval, how many times the timer was triggered for the Console application
    • Win Form :  At the given milli sec interval, how many times the timer was triggered for the Win Form application

    Here is the code for the console app.

    using System;
    
    namespace SystemTimersConsole
    {
        class Program
        {
    
            // Ticks on 1 second intervals, Used to write _tpsCount every second
            private System.Timers.Timer  _oneSecondTimer;
    
            // Ticks on user specified interval, used to update _tpsCount variable
            private System.Timers.Timer  _tpsTimer;
    
            // Represends how many times in 1 second _tpsTimer is fired off. 
            private int _tpsCount;
    
            // Since timers run in differnt treads than GUI thread, need a delegate to 
            // update the GUI
            public delegate void DisplayTPS( object source, System.Timers.ElapsedEventArgs e );
    
    
            static void Main( string[ ] args )
            {
                Program p = new Program();
                p.Reset( );
    
                // Param specifies millisecond frequency for _tpsTimer
                p.StartTimers( 19 );
                ConsoleKeyInfo i = Console.ReadKey( );
            }
    
    
    
            /// <summary>
            /// _oneSecondTimer tick event. 
            /// Updates the list box with the TPS count information. Function can be called from either 
            /// the GUI thread or one one of timer threads.
            /// </summary>
            /// <param name="state">Not using this state</param>
            private void DisplayData( Object source, System.Timers.ElapsedEventArgs e )
            {
                Console.WriteLine( "TPS COUNT : " + _tpsCount );
                _tpsCount = 0;
            }
    
    
    
            /// <summary>
            /// _tpsTimer tick event handler. Increments the TPS counter indicator.
            /// </summary>
            /// <param name="state"></param>
            private void IncTPSCount( Object source, System.Timers.ElapsedEventArgs e )
            {
                _tpsCount++;
            }
    
            /// <summary>
            /// Resets all the controls and what not.
            /// </summary>
            private void Reset( )
            {
                if( _oneSecondTimer != null )
                    _oneSecondTimer.Dispose( );
                if( _tpsTimer != null )
                    _tpsTimer.Dispose( );
    
                _oneSecondTimer = null;
                _tpsTimer       = null;
                _tpsCount       = 0;
            }
    
    
            /// <summary>
            /// Resets controls, timers and restarts timers.
            /// </summary>
            /// <param name="milli">Specifies in milliseconds how frequent to call IncCount</param>
            private void StartTimers( int milli )
            {
                Reset( );
                _oneSecondTimer         = new System.Timers.Timer( 1000 );
                _tpsTimer               = new System.Timers.Timer( milli );
    
                _oneSecondTimer.Elapsed += DisplayData;
                _tpsTimer.Elapsed       += IncTPSCount;
    
                _oneSecondTimer.Enabled = true;
                _tpsTimer.Enabled       = true;
            }
        }
    }

    Here is the code for the Windows Form app
    using System;
    using System.Windows.Forms;
    using System.Threading;
    
    
    /* ==============================================================================================
     * Shows how to use two the System.Timers.Timer class instances to create a to display how many
     * times a timer will trigger in one second. 
     *  
     * _tpsTimer triggers on a user specified value in the GUI. It will update _tpsCount which
     * holds the number of times _tpsTimer was triggered.
     * 
     * _oneSecondTimer will trigger every 1 second and will display the _tpsTimer value in the
     * list box showing how many times _tpsTimer was triggered in 1 second.
     * 
     * These methods can be used to try and control how fast something happens within 1 second.  This
     * is not SCIENTIFICALLY accurate, but it might be good enough to throttle down something like 
     * Frames Per Second to prevent flooding the network with too much streaming media. 
     * 
     * Because the timers run in a separate thread, the DisplayData function must use multithreading
     * techniques to allow cross threaded data to be used in the GUI.
     ============================================================================================= */
    
    
    namespace FPSTimer
    {
        public partial class FormSystemTimers : Form
        {
            // Ticks on 1 second intervals, used to trigger list box updates
            private System.Timers.Timer  _oneSecondTimer;
    
            // Ticks on user specified interval, used to update _tpsCount variable
            private System.Timers.Timer  _tpsTimer;
    
            // Represends how many times in 1 second _tpsTimer is fired off. 
            private int _tpsCount;
    
            // Since timers run in differnt treads than GUI thread, need a delegate to 
            // update the GUI
            public delegate void DisplayTPS( object source, System.Timers.ElapsedEventArgs e );
    
    
            public FormSystemTimers( )
            {
                InitializeComponent( );
    
                // Set initial state
                Reset( );
            }
    
            /// <summary>
            /// _oneSecondTimer tick event. 
            /// Updates the list box with the TPS count information. Function can be called from either 
            /// the GUI thread or one one of timer threads.
            /// </summary>
            /// <param name="state">Not using this state</param>
            private void DisplayData( Object source, System.Timers.ElapsedEventArgs e )
            {
    
                if( this.lb1.InvokeRequired )
                {
                    // Create the delegate and specify which function to call.
                    DisplayTPS d = new DisplayTPS( DisplayData );
                    this.Invoke( d, new object[ ] { source, e } );
                }
                else
                    lb1.Items.Add( "TPS COUNT : " + _tpsCount );
    
                _tpsCount = 0;
            }
    
    
    
            /// <summary>
            /// _tpsTimer tick event handler. Increments the TPS counter indicator.
            /// </summary>
            /// <param name="state"></param>
            private void IncTPSCount( Object source, System.Timers.ElapsedEventArgs e )
            {
                _tpsCount++;
                Console.WriteLine( _tpsCount );
            }
    
    
            /// <summary>
            /// Resets all the controls and what not.
            /// </summary>
            private void Reset( )
            {
                if( _oneSecondTimer != null )
                    _oneSecondTimer.Dispose( );
                if( _tpsTimer != null )
                    _tpsTimer.Dispose( );
    
                _oneSecondTimer = null;
                _tpsTimer       = null;
    
                lb1.Items.Clear( );
                _tpsCount = 0;
    
                if( tbMilli.Text == "" )
                    tbMilli.Text = "30";
            }
    
    
            /// <summary>
            /// Resets controls, timers and restarts timers.
            /// </summary>
            /// <param name="milli">Specifies in milliseconds how frequent to call IncCount</param>
            private void StartTimers( int milli )
            {
                Reset( );
                _oneSecondTimer = new System.Timers.Timer( 1000 );
                _tpsTimer       = new System.Timers.Timer( milli );
    
                _oneSecondTimer.Elapsed += DisplayData;
                _tpsTimer.Elapsed       += IncTPSCount;
    
                _oneSecondTimer.Enabled = true;
                _tpsTimer.Enabled = true;
            }
    
            /// <summary>
            /// 
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void btnStart_Click( object sender, EventArgs e )
            {
                if( tbMilli.Text == "" )
                    return;
    
                int milli = Int32.Parse( tbMilli.Text );
    
                if( milli > 0 && milli < 1001 )
                    StartTimers( milli );
            }
    
    
            /// <summary>
            /// Calling this to kill the timers. If you don't do this, they continue to run after the
            /// form starts to shut down which causes an exception. This is because the timer will still
            /// try to call their tick handlers (updating the list), but the form controls have been killed, 
            /// so an exception happens.  So, kill everyting BEFORE allowing the form to close.
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void Form1_FormClosing( object sender, FormClosingEventArgs e )
            {
                Reset( );
            }
        }
    }

    Any feedback is greatly appreciated!

    Thanks
    Rick


    Rick



    • Edited by m00n Thursday, February 14, 2019 11:17 PM
    Thursday, February 14, 2019 11:12 PM

All replies

  • Maybe it is influenced by Console.WriteLine in your form. You can move it to DisplayData.

    By the way, since this kind of timer uses threads, theoretically you should use Interlocked.Increment instead of ‘++’.

    Friday, February 15, 2019 7:22 AM
  • No, there's not that much difference.  There are some things to remember, however.

    Timers are only checked at scheduler intervals, which run at the system clock tick.  By default, that happens every 15.625ms.  That's why you're seeing no more than about 60 hits per second.

    It's possible there was something else hogging the CPU when your GUI app ran.  Your timer handler isn't guaranteed to run IMMEDIATELY upon expiration.  Instead, it sets a flag that gets set later.  If your GUI is busy, or the CPU was busy, you might not see it right away.


    Tim Roberts | Driver MVP Emeritus | Providenza &amp; Boekelheide, Inc.

    Friday, February 15, 2019 7:27 AM
  • Maybe it is influenced by Console.WriteLine in your form. You can move it to DisplayData.

    By the way, since this kind of timer uses threads, theoretically you should use Interlocked.Increment instead of ‘++’.

    Good point, I should have locked that increment counter. Three different threads are trying to access it. 

    Rick

    Friday, February 15, 2019 5:04 PM
  • No, there's not that much difference.  There are some things to remember, however.

    Timers are only checked at scheduler intervals, which run at the system clock tick.  By default, that happens every 15.625ms.  That's why you're seeing no more than about 60 hits per second.

    It's possible there was something else hogging the CPU when your GUI app ran.  Your timer handler isn't guaranteed to run IMMEDIATELY upon expiration.  Instead, it sets a flag that gets set later.  If your GUI is busy, or the CPU was busy, you might not see it right away.


    Tim Roberts | Driver MVP Emeritus | Providenza &amp; Boekelheide, Inc.

    I didn't realize that they are scheduled. I assumed they were fired off immediately, nor did I know they only happen every 15.x ms.

    Rick

    Friday, February 15, 2019 5:05 PM
  • > I assumed they were fired off immediately.

    No, you can imagine the chaos that would ensue if there were a CPU interrupt every time anyone's timer expired.  Instead, the systems timer interrupt occurs every 15.6ms.  That causes the scheduler to run.  It scans through the list of pending timers and asks, "has this one expired yet?"  If it has expired, it sets an event.  Your main message loop checks that event, and if it has fired, it dispatches a WM_TIMER message, which eventually calls your callback.


    Tim Roberts | Driver MVP Emeritus | Providenza &amp; Boekelheide, Inc.

    Friday, February 15, 2019 11:15 PM