Performance of synchronization with GUI thread

Posted on | December 20, 2011 | No Comments

It is well known that all graphical controls should work in one thread. There are many articles on this subject, so we shall not repeat them. In multi-threaded applications every call should be synchronized with the main thread containing windows message loop. Control is a base class that provides Control.Invoke and Control.BeginInvoke methods. The first of these methods blocks the calling thread until the code contained in delegate is executed. The second method blocks the calling thread only for the time of adding delegate in queue. Execution of this delegate code is done in parallel in GUI thread.

Both Control.Invoke and Control.BeginInvoke methods may accept different delegate types and their parameters. However, this causes a serious performance issue as the main code calling method is Delegate.DynamicInvoke(params object[] args), which in turn uses low-performance reflection. At the same time, for some delegates such as EventHandler, MethodInvoker and WaitCallback the code is called directly. In the time of code execution the delegate is checked for belonging to one of the above types with specified number of parameters, and if it doesn’t – DynamicInvoke() is called.

From practical point of view the synchronization process should look as follows:

    someControl.BeginInvoke(new MethodInvoker(delegate
    {
        //a code here
    }));

 

For comparison, if custom delegates are used, performance of Invoke call from non-GUI thread equals on the average:

Custom delegate: 25,000 calls per second.
MethodInvoker: 40,000 calls per second.

If Control.Invoke is called from GUI thread that doesn’t count inter-thread synchronization rate and doesn’t have context switches, the results are as follows:

Custom delegate: 150,000 calls per second
MethodInvoker:  1,200,000 calls per second.

The example code is provided below:

    ThreadPool.QueueUserWorkItem(delegate
    {
        int k = 0;

        DateTime dt = DateTime.UtcNow;
        for (int i = 0; i < 10000; ++i)
        {
            //CustomDelegate d = delegate { k++; };
            //Invoke(d, new object[]{EventArgs.Empty});

            Invoke(new MethodInvoker(delegate { k++; }));
        }
        TimeSpan ts = DateTime.UtcNow - dt;

        MessageBox.Show(string.Format("{0} invokes. Perf = {1} call/sec", k, 1000*k/ts.TotalMilliseconds));
    });

 

Summary. To improve performance it is always better to use MethodInvoker that improves performance by 50% when calling Control.Invoke method from non-GUI thread and tenfold when calling from GUI thread.

Comments

Leave a Reply