【问题标题】:What are the reasons why the CPU usage doesn’t go 100% with C# and APM?使用 C# 和 APM 时 CPU 使用率没有达到 100% 的原因是什么?
【发布时间】:2010-02-23 18:13:35
【问题描述】:

我有一个 CPU 密集型应用程序。在单个线程上处理数据时,CPU 使用率会持续数分钟达到 100%。因此,应用程序的性能似乎受到 CPU 的约束。我对应用程序的逻辑进行了多线程处理,从而提高了整体性能。但是,CPU 使用率几乎不会超过 30%-50%。我希望 CPU(和许多内核)达到 100%,因为我同时处理许多数据集。

下面是我用来启动线程的逻辑的简化示例。当我运行这个示例时,CPU 达到 100%(在 8/16 核机器上)。但是,我使用相同模式的应用程序却没有。

public class DataExecutionContext
{
    public int Counter { get; set; }

    // Arrays of data
}

static void Main(string[] args)
{
    // Load data from the database into the context
    var contexts = new List<DataExecutionContext>(100);
    for (int i = 0; i < 100; i++)
    {
        contexts.Add(new DataExecutionContext());
    }

    // Data loaded. Start to process.
    var latch = new CountdownEvent(contexts.Count);
    var processData = new Action<DataExecutionContext>(c =>
    {
        // The thread doesn't access data from a DB, file, 
        // network, etc. It reads and write data in RAM only 
        // (in its context).
        for (int i = 0; i < 100000000; i++)
            c.Counter++;
    });

    foreach (var context in contexts)
    {
        processData.BeginInvoke(context, new AsyncCallback(ar =>
        {
            latch.Signal();
        }), null);
    }

    latch.Wait();
}

我已将锁的数量减少到严格的最小值(只有闩锁处于锁定状态)。我发现最好的方法是创建一个线程可以在内存中读/写的上下文。上下文不在其他线程之间共享。线程无法访问数据库、文件或网络。换句话说,我分析了我的应用程序,但没有发现任何瓶颈。

为什么我的应用程序的 CPU 使用率没有达到 50% 左右?是我使用的模式吗?我应该创建自己的线程而不是使用 .Net 线程池吗?有什么陷阱吗?有什么工具可以推荐给我来查找我的问题吗?

谢谢!

【问题讨论】:

  • 可能,但不太可能是您的代码触发了大量垃圾收集,这需要一定程度的同步。

标签: c# .net multithreading asynchronous


【解决方案1】:

有很多因素可能导致这种行为。

首先,你有什么类型的 CPU?如果你有一个 i7 或类似的处理器,操作系统会认为它有 8 个内核,而实际上,它有 4 个内核和 2 个超线程/内核。对于大多数操作,超线程并不能真正提供与第二个内核相同的可扩展性,即使操作系统以这种方式看待它。这导致我的整体 CPU 使用率在操作系统看来较低...

其次,您可能会发生某种形式的真正共享。你提到你有锁定 - 即使它保持在最低限度,锁定可能会阻止你有效地安排这个。

另外,现在,您正在预先安排所有 100 个工作项。操作系统将不得不分页进出这 100 个线程。您可能希望将此限制为仅允许在给定时间处理特定数量。使用新的任务并行库更容易(只需使用 Parallel.ForEach 和 ParallelOptions 设置来获得最大线程数) - 但可以自己完成。

鉴于您计划同时处理所有 100 个项目,分页可能会妨碍获得最大吞吐量的能力。

此外,如果您正在做任何其他“更真实”的工作 - 您可能会遇到错误的共享问题,尤其是在您使用共享的数组或集合时(即使您正在处理的元素不是共享)。

我建议在 VS 2010 的并发分析器下运行它 - 它会让您更清楚地了解正在发生的事情。

【讨论】:

  • 我在很多机器上试过:Core Duo(使用率 ~80-90%)、i7(使用率 ~50%)、Dual Xeon L5520(使用率 ~40-50%)。
  • 由于您在核心二重奏上的比例为 80-90%,而在其他二重奏上的比例较低,听起来您有虚假或真实的共享问题。如果您尝试使用来自多个线程的内存中过于紧密的数据,则可能会发生错误共享 - 真正的共享是由于锁定...
  • 我在 MSDN 上发现了一篇关于 False Sharing (msdn.microsoft.com/en-us/magazine/cc872851.aspx) 的文章,看起来这可能是我的问题……我还不确定是否完全理解 False Sharing。你是怎么想到的?
  • 我会看 Igor Ostrovky 的 PDC 会议:igoro.com/archive/video-of-my-plinq-session-at-pdc-2009 他详细描述了虚假共享,并举了一个很好的例子,并谈到了如何解决它。
【解决方案2】:

这是在没有看到您的应用程序的情况下进行的推测,但如果您的应用程序正在处理文件、数据库、创建大量对象(请求内存)、使用网络设备或任何类型的硬件设备,那么这些因素可能限制您的应用程序达到 100% 的 CPU 使用率。这与线程切换相结合也可能是一个因素。

您说您正在使用您给出的示例的模式,但您说该示例达到 100% 的利用率,但您的应用程序没有。所以那里有一些区别,你应该尝试更详细地描述你的应用程序正在做什么。 50%的利用率还不错。许多应用程序在超线程英特尔 CPU 上以 50% 的速度运行,它们仍然运行良好。如果应用程序没有达到 100% 的 cpu 利用率并且您仍然获得良好的性能,那么我会说这实际上是一件好事,因为这意味着您有一些空间,因为它不再受 CPU 限制。这意味着其他事情可能会占用 CPU 时间的情况下,您的应用程序不会受到太大影响。如果它的利用率为 100%,那么当其他进程积极使用 CPU 时,您会看到应用程序性能波动。

【讨论】:

    【解决方案3】:

    如果您要分配大量小内存 - 托管堆可能会成为共享资源,从而阻塞线程并减慢进程并因此降低 CPU 使用率

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-16
      • 1970-01-01
      • 2017-01-29
      • 1970-01-01
      相关资源
      最近更新 更多