什么更快、更有效 - BeginInvoke 还是 SynchronizeContext.Post?

3

有没有人试过找出哪个更快,更有效率(创建的对象较少,因此涉及的GC较少)-control.BeginInvoke还是SynchroniseContext.Post?

WPF、C#、.NET 4

我希望得到实际支持而不是“我认为”或“我在某个地方听说过”的回复。

干杯

P.S. 我要向几个控件发布大量消息,我希望它尽可能高效快速(每秒几百次更新)。我知道.NET可以处理这个问题(我以前做过),但现在我希望它尽可能快...


就我所知,WPF仅接受委托作为Dispatcher.Begin/Invoke方法的参数。然后需要使用Delegate.Invoke在消息循环中调用它,这归结为反射调用,与直接委托调用相比速度要慢得多。Windows Forms有一种特殊的委托类型称为MethodInvoker,在消息循环中得到特殊处理以防止此开销。但是,由于您正在使用WPF并且希望快速...祝您好运。 - Alois Kraus
如果你是对的,那确实是个坏消息... - Boppity Bop
为什么不批量更新呢?将所有更新委托排队,每秒执行几次BeginInvoke即可。 - adrianm
2个回答

4
首先,在WPF中没有Control.BeginInvoke(那是你想到的winforms)。其次,SynchronizationContext是当前平台提供的任何同步机制的抽象。在WPF的情况下,它是对Dispatcher的抽象。理论上,使用这种抽象而不是直接使用Dispatcher会付出一些代价。但是这种抽象有一个很好的原因——有时您需要编写独立于平台的线程同步代码。如果您不需要,那么尽管直接使用Dispatcher

抛开我的 typ0 控制/调度器... 我真正需要知道的是 - BeginInvoke 是否比 Post 更耗费资源并且/或者创建更多的内部对象... 我假设 Post 所做的就是旧版 Windows 消息泵所做的事情 - 将一个小对象放入队列中... 我的假设是否正确,如果是的话 - BeginInvoke 的工作量有多大?谢谢 - Boppity Bop
似乎BeginInvoke比Post稍微快一些。在没有延迟的情况下循环50000条消息,我在我的慢速笔记本电脑上获得了大约15个msg/ms的性能。 - Boppity Bop
顺便说一下,15条消息/毫秒并不是您实际获得的性能。您基本上测量了消息可以放入队列的速度,而不是更新UI的速度。真正的消息循环性能大约为100-1000条消息/秒,具体取决于您在实际调用中绘制了多少内容。 - Alois Kraus
不行,消息的发送速度可以更快。我调整了循环后的延迟时间,以找出循环结束和屏幕上最后一次渲染之间的大致延迟时间。对于50K采样集,误差是可以容忍的。因此,我将该延迟(最后一条消息发布和屏幕上显示的最后一条消息之间的延迟)进行了因式分解,并得到了15个消息/毫秒。 - Boppity Bop
更正:我可以在i7 PC上的WPF textBlock中获得每毫秒35个消息渲染。 - Boppity Bop

2

在我的i7桌面上,BeginInvoke比SynchronizationContext.Post快42.8%。

结果如下:

Post      Send      Diff ms   Ratio
1280866   925416    35.00     -38.4%
1192232   916251    27.00     -30.1%
1338990   876215    46.00     -52.8%
1394783   863241    53.00     -61.6%
1332485   1046789   28.00     -27.3%
1335241   895784    43.00     -49.1%
1267470   1064894   20.00     -19.0%
1308461   884136    42.00     -48.0%
1321243   850704    47.00     -55.3%
1313230   896469    41.00     -46.5%

代码:

这段代码:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.RealTime;
    }

    Thread th;
    DispatcherSynchronizationContext ctx;
    protected override void OnContentRendered(EventArgs e)
    {
        base.OnContentRendered(e);

        Thread.CurrentThread.Priority = ThreadPriority.Highest;

        ctx = new DispatcherSynchronizationContext(this.Dispatcher);
        th = new Thread(Start);
        th.Start();
    }

    int MACRO = 10;
    int TESTS = 10;
    int LOOPS = 50000;
    void Start()
    {
        Thread.CurrentThread.Priority = ThreadPriority.AboveNormal;

        // flush just in case
        for (int i = 0; i < 100; i++)
        {
            ctx.Post(Callback, 9999999);
            this.Dispatcher.BeginInvoke(
                new Action<object>((object state) => { txt2.Text = state.ToString(); }), 
                    DispatcherPriority.Send, 9999999);                
        }

        Thread.Sleep(1000);

        // results
        List<Tuple<long, long>> results = new List<Tuple<long, long>>();

        // actual test
        for (int x = 0; x < MACRO; x++)
        {
            Stopwatch sw = new Stopwatch();

            // sync context post
            long tick1, tick2;
            for (int i = 0; i < TESTS; i++)
            {
                sw.Start();
                for (int j = i; j < LOOPS + i; j++)
                {
                    ctx.Post(Callback, j);
                }
                sw.Stop();

                Thread.Sleep(1500);
            }

            tick1 = sw.ElapsedTicks;

            // begin invoke
            sw.Reset();
            for (int i = 0; i < TESTS; i++)
            {
                sw.Start();
                for (int j = i; j < LOOPS + i; j++)
                {
                    this.Dispatcher.BeginInvoke(
                        new Action<object>((object state) => { txt2.Text = state.ToString(); }), 
                            DispatcherPriority.Normal, j);
                }
                sw.Stop();

                Thread.Sleep(1500);
            }

            tick2 = sw.ElapsedTicks;

            // store results
            results.Add(new Tuple<long, long>(tick1, tick2));

            // display to make it less boring
            this.Dispatcher.BeginInvoke(new Action(() => { txt3.Text += string.Format("{0} {1}. ", tick1, tick2); }));
            Thread.Sleep(100);                
        }

        StringBuilder sb = new StringBuilder();
        foreach (var res in results)
            sb.AppendLine(string.Format("{0}\t{1}\t{2:0.00}\t{3:0.0%}", 
                res.Item1, res.Item2, (res.Item1 - res.Item2) / 10000, res.Item2 != 0 ? 1.0 - res.Item1 / (double)res.Item2 : 0.0));

        this.Dispatcher.BeginInvoke(
            new Action(() => { txb1.Text = sb.ToString(); }));                

    }

    void Callback(object state)
    {
        txt1.Text = state.ToString();
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接