当前位置:  开发笔记 > 编程语言 > 正文

在.Net native中的线程池上运行异步任务的性能很差

如何解决《在.Netnative中的线程池上运行异步任务的性能很差》经验,为你挑选了1个好方法。

我发现托管vs .Net本机代码有一个奇怪的区别.我有一个繁重的工作重定向到线程池.当在托管代码中运行应用程序时,一切都运行顺畅,但是一旦我打开本机编译 - 任务运行速度慢几十倍,以至于它挂起UI线程(我猜CPU是如此过载).

以下是调试输出的两个屏幕截图,左侧的屏幕截图来自托管代码,右侧的屏幕截图来自本机编译.正如您所看到的,UI任务所消耗的时间在两种情况下几乎相同,直到启动线程池作业时 - 然后在托管版本中UI经过的时间增长(实际上UI被阻止,您无法采取任何操作).线程池工作的时间不言自明.

管理本地人

重现问题的示例代码:

private int max = 2000;
private async void UIJob_Click(object sender, RoutedEventArgs e)
{
    IProgress progress = new Progress((p) => { MyProgressBar.Value = (double)p / max; });
    await Task.Run(async () => { await SomeUIJob(progress); });
}

private async Task SomeUIJob(IProgress progress)
{
    Stopwatch watch = new Stopwatch();
    watch.Start();
    for (int i = 0; i < max; i++)
    {
        if (i % 100 == 0) { Debug.WriteLine($"     UI time elapsed => {watch.ElapsedMilliseconds}"); watch.Restart(); }
        await Task.Delay(1);
        progress.Report(i);
    }
}

private async void ThreadpoolJob_Click(object sender, RoutedEventArgs e)
{
    Debug.WriteLine("Firing on Threadpool");
    await Task.Run(() =>
   {
       double a = 0.314;
       Stopwatch watch = new Stopwatch();
       watch.Start();
       for (int i = 0; i < 50000000; i++)
       {
           a = Math.Sqrt(a) + Math.Sqrt(a + 1) + i;
           if (i % 10000000 == 0) { Debug.WriteLine($"Threadpool -> a value = {a} got in {watch.ElapsedMilliseconds} ms"); watch.Restart(); };
       }
   });
    Debug.WriteLine("Finished with Threadpool");
}

如果您需要完整的样本 - 那么您可以在此处下载.

正如我所测试的那样,在调试和发布版本中,优化/非优化代码都会出现差异.

有没有人知道什么会导致这个问题?



1> Matt Whilden..:

导致此问题的原因是"ThreadPool"数学循环导致GC饥饿.从本质上讲,GC已经决定它需要运行(由于想要进行一些互操作分配)并且它试图阻止所有线程进行收集/压缩.不幸的是,我们还没有添加.NET Native能够劫持热循环的能力,就像你下面的那样.有关将.NET应用商店应用迁移到.NET Native页面的简要说明如下:

无需在任何线程上进行调用(例如,while(true);)的无限循环可能会使应用程序停止.同样,大量或无限等待可能会使应用程序停止运行.

解决此问题的一种方法是在循环中添加一个调用站点(GC非常乐意在尝试调用另一个方法时中断您的线程!).

    for (long i = 0; i < 5000000000; i++)
           {
               MaybeGCMeHere(); // new callsite
               a = Math.Sqrt(a) + Math.Sqrt(a + 1) + i;
               if (i % 1000000000 == 0) { Debug.WriteLine($"Threadpool -> a value = {a} got in {watch.ElapsedMilliseconds} ms"); watch.Restart(); };
    }

...

    [MethodImpl(MethodImplOptions.NoInlining)] // need this so the callsite isn’t optimized away
    private void MaybeGCMeHere()
    {
    }

缺点是你会有这种"丑陋"的黑客攻击,你可能会受到添加指令的影响.我让这里的一些人知道,我们认为这件事"非常罕见"实际上是由客户打击,我们会看到可以做些什么.

谢谢你的报道!

更新:我们围绕这种情况做了一些重大改进,并且能够劫持大多数长期运行的GC线程.这些修复程序可能会在4月份的Update 2 UWP工具集中提供吗?(我不控制出货时间表:-))

更新更新:新工具现已作为UWP工具1.3.1的一部分提供.我们不希望有一个完美的解决方案来积极地反对被GC劫持的线程,但我希望使用最新工具可以更好地利用这种情况.让我们知道!


感谢整个团队对此进行处理.你的一位同事说,这将在下一次VS更新中得到纠正 - 这很好.我同意我很难在桌面上重现这个问题,但在ARM上我认为这是一个真实的场景 - 实际上我已经在我的应用程序中观察到了这一点.我有一个处理照片的方法,并对像素进行一些数学运算,因为它是cpu消耗,它被重定向到线程池,这是我发现问题的地方.再一次谢谢你.
推荐阅读
Life一切安好
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有