none
Why is the Dispatcher so slow?

    Question

  • Hello,

     

    I have a tool, that reads data from my harddrive with 1 thread and uses 4 parallel running threads which analyze the data. The 4 threads have each a queue, which gets filled by the reader thread. The size of the queues is each 20.000 elements.

    Now I wrote a GUI to visualize the fill level of the 4 queues. Is used the Dispatcher to pass the fill level information to the GUI thread.

    But unfortunately the Dispatcher is slow like a slug.

    The 4 threads analyze about 300.000 data sets a second and so the queues fill level changes 300.000 / 4 times per second per queue. If I send every change of the fill level through the dispatcher my performance goes down to only 10.000 data sets a second. If I update the GUI less, only if the fill level changes for more then 100 elements, the analyzers speed up to 100.000 data sets a second.

    So, what can I do, to update my GUI every time, the fill level of my queue changes, without loosing performance. Isn't there a way to kick out the fu... Dispatcher? Or do some magic, that makes the Dispatcher run like a Ferrari?

     

     

    Friday, April 23, 2010 11:41 PM

Answers

  • I just found the solution on google!

     

    You have to use the DispatcherTimer. Your GUI has to pull the needed information. The workers should not push the information into the GUI.

     

    Now I created an array with the fill levels of my queues. The array is hostet in a class, that manages the analyzer threads. I configured the DispatcherTimer to call every 10ms a callback. The DispatcherTimer belongs to the GUI thread and so does not need to call Dispatcher.Invoke() to access a GUI Control. Inside the callback method I pull out the current filllevels of the queues and update my GUI.

    Now there is no Dispatcher.Invoke(). And it is fast. Great :-D

     

    PULL!!!! Not PUSH!! :-)

    • Marked as answer by alexfiftyfour Saturday, April 24, 2010 12:38 AM
    Saturday, April 24, 2010 12:38 AM

All replies

  • Dispatcher is just the UI thread. Just like your other four. If you interrupt any of your other threads and ask them to do something 300.000 / second, they'll get slugish too.

    The principle here is that you can't have the cake and eat. There are two things you can do to optimize this:

    1. Use Dispatcher.BeginInvoke() method, instead of Dispatcher.Invoke(). This way, the calling thread will not way for the request to be completed and will work, almost but not quite, like an asynchronous request without response.

    2. Use a Dispatcher.BeginInvoke() method overload that receives a DispatcherPriority. Like this one:
    http://msdn.microsoft.com/en-us/library/ms591206(v=VS.100).aspx

    If you read the documentation about, DispatcherPriority enumerator, you'll find that you have many options to try and make the change more fluid.
    http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcherpriority(v=VS.100).aspx
    I suggest you try at least from ContextIdle to Input although you should really try them all.

    Or course, the change will never show 300.000 times per second. Your screen might do 80 refreshes per second but your eye won't see more that 30 even when you're in the zone.

    Trade-off, trade-off.


    Bigsby, Lisboa, Portugal
    O que for, quando for, é que será o que é...
    Wenn ist das Nunstruck git und Slotermeyer? Ja! ... Beiherhund das Oder die Flipperwaldt gersput!
    http://bigsby.eu
    Saturday, April 24, 2010 12:34 AM
  • I just found the solution on google!

     

    You have to use the DispatcherTimer. Your GUI has to pull the needed information. The workers should not push the information into the GUI.

     

    Now I created an array with the fill levels of my queues. The array is hostet in a class, that manages the analyzer threads. I configured the DispatcherTimer to call every 10ms a callback. The DispatcherTimer belongs to the GUI thread and so does not need to call Dispatcher.Invoke() to access a GUI Control. Inside the callback method I pull out the current filllevels of the queues and update my GUI.

    Now there is no Dispatcher.Invoke(). And it is fast. Great :-D

     

    PULL!!!! Not PUSH!! :-)

    • Marked as answer by alexfiftyfour Saturday, April 24, 2010 12:38 AM
    Saturday, April 24, 2010 12:38 AM
  • I don't mean to bother but I don't think that you should be so happy just because it works when your issue is a matter of principle.

    You were interrupting 300.000 times per second a thread just like any other that, by the way, does much more then maths because it has to render and provide data for the GPU to display, and asking that thread to stop what it was doing, because it will need to recalculate to update as you requested, and start all over again what ever it was it was doing.

    I say the numbers you provided in your thread starter are pretty amazing. I bet you're not working on a 286 nor your GPU is older than 3 years.

    And I'd say the Dispatcher is doing a hack of a job.

    Ever wondered why TaskManager performance graphs don't update at clock time?
    Of course pulling is the best option for the performance of the application whose most important task should be the processing. If you want the fastest and non-process delaying UI update, the option I provided is the way to go.

    More important than your choice is that you realize the concepts at stake and make a educated and conscient choice.

    Best regards.


    Bigsby, Lisboa, Portugal
    O que for, quando for, é que será o que é...
    Wenn ist das Nunstruck git und Slotermeyer? Ja! ... Beiherhund das Oder die Flipperwaldt gersput!
    http://bigsby.eu
    • Edited by Bigsby Saturday, April 24, 2010 12:50 AM typo
    Saturday, April 24, 2010 12:49 AM
  • Good morning Bigsby,

    BeginInvoke() is not a good idea. It is an asynchron call, that means, if you call it 300.000 times a second, then it will not slow down the analyzers, it will execute all 300k calls, BUT it will not execute the calls in time.

    So, if the analyzers have finished after 2 minutes, the GUI is still updating and executing the BeginInvoke() calls. And if you have 30 million of data rows, which you analyze, then the GUI keeps updating itself for an hour and more, although the analyzers have finished after a few minutes. And this technique eats up all your memory.

    Fact is, that calls to Dispatcher.Invoke() or Dispatcher.BeginInvoke() are extreme slow. I searched google and a guy benchmarked the dispatcher. Updating his GUI without Dispatcher: 30ms. With Disptacher: 6000ms.

    Microsoft: Kick the Dispatcher out, or optimize it! Please!!

     

    So, I think the only applicable solution is, that you use the DispatcherTimer class to get every x seconds/milliseconds called back and then pull the actual status out of the queues. This way you can update the GUI at the GUI threads maximum speed, that is possible and you don't slow down your workers or have BeginInvoke side effects. I tried DispatcherTimes from 1 to 40 ms. A good value was something arround 10ms. 40ms = 25fps. But its slow.

    Saturday, April 24, 2010 9:38 AM
  • I agree with you, that updating a GUI 300k times per sec. is not a good idea. But as I wrote above, the Dispatcher is realy slow (30ms vs. 6000ms) and allows no usefull update frequency for such an application.

    I had a configurable filter in my workerthreads for adjusting the update frequency. With that I could test, what is a good update interval and it was about 200ms, if you use the Dispatcher. With Invoke() it slowed down the threads, but not so dramatically (300k down to 100k). As written above, BeginInvoke() is useless in this scenario.

     

    But there is an other thing, I'm thinking about. Is it faster to do this:

     

    public event ProgressChangedDelegate ProgressChanged;

    private int counter = 0;

    while(counter < 1Mio)

    {
         counter++;

         if(counter % 1000 == 0) ProgressChanged(counter);
    }

     

    or is this faster:

    public int counter = 0;

    while(counter < 1Mio)

    {
        counter ++;
    }

     

    In case 1 the other thread(GUI) gets informed by the event, that the progress has changed (and has to use Dispatcher.Invoke). In case 2 the other thread uses DispatcherTimer to pull the actual state out of the counter variable every X ms.

    In case 2 you might need some sync, but the GUI thread accesses the counter only in a reading manner, an the provided data will only be used to update a progressbar, so it wouldn't matter if there occure some multithreading side effects. Or is a varable of type int allways changed and read in an atomic way? I don't know this. Mybee someone has an answer to that.

     

     

    Saturday, April 24, 2010 10:01 AM
  • If you set DispatcherPriority correctly, BeginInvoke will seem as asynchronous to the calling thread and it won't wait for the delegate to end, you can set it in a priority that the Dispatcher will only execute it if it can and not all the 300.000 calls.

    The scenario you are describing in your last post has a name in .Net and is called System.ComponentModel.BackgroundWorker. This class launches a background thread and has events that "happen" on the calling thread like ProgressChanged or RunWorkerComplete.


    Bigsby, Lisboa, Portugal
    O que for, quando for, é que será o que é...
    Wenn ist das Nunstruck git und Slotermeyer? Ja! ... Beiherhund das Oder die Flipperwaldt gersput!
    http://bigsby.eu
    Saturday, April 24, 2010 10:12 PM