性能不佳的多线程应用程序的常见模式
并发可视化工具有助于开发人员使多线程应用程序的行为可视化。 本工具包括性能不佳的多线程应用程序的常见模式库。 该库包括通过该工具公开的典型、可识别的可视化模式,同时还包括每个模式所表示的行为的说明、该行为的可能结果,以及最常见的解决方法。
锁争用和顺序执行
尽管并行化应用程序有多个线程并且计算机具有足够数量的逻辑内核,但该程序有时依然会按顺序继续执行。 第一个症状是多线程性能很差,甚至可能比串行实现还稍慢一些。 在线程视图中,您看不到以并行方式运行的多个线程;相反,在任何时间都只能看到仅有一个线程正在执行。 此时,如果单击线程中的同步段,您可以看到阻塞的线程的调用堆栈(阻塞调用堆栈)和移除阻塞状况的线程(解除阻塞调用堆栈)。 此外,如果解除阻塞的调用堆栈发生在您所分析的进程中,则显示线程就绪连接符。 此后,您可以从阻塞和解除阻塞的调用堆栈导航到您的代码,以进一步调查序列化的原因。
如下图所示,并发可视化工具还可以在“CPU 使用率”视图中公开此症状,其中,尽管存在多个线程,但应用程序仅使用一个逻辑内核。
有关更多信息,请参见 MSDN 博客网站上 Hazim Shafi 的博客 Parallel Performance Tools For Windows(Windows 的并行性能工具)中的“Performance Pattern 1: Identifying Lock Contention”(性能模式 1:识别锁争用)。
工作负荷分配不均
当应用程序中不同并行线程间的工作分配无规律时,在每个线程完成其工作时将出现一个典型的楼梯模式,如上图所示。对于每个并发线程,并发可视化工具通常会显示非常接近的启动时间。 但是,这些线程通常以非常规的方式结束,而非同时结束。 此模式指示一组并行线程间的工作分配无规律,这可能导致性能下降。 对于此类问题,最佳解决方法是重新计算用来在并行线程间分配工作的算法。
如下图所示,并发可视化工具还可以在“CPU 使用率”视图中公开此症状,此时 CPU 使用率正逐步下降。
过度订阅
在过度订阅的情况下,进程中的活动线程数大于系统上可用的逻辑内核数。 上图显示了过度订阅的结果,其中表明所有活动线程中都有重要的抢占分级。 另外,据图例显示,大部分时间都用在了抢占上(在此示例中为 84%)。 这可能表示进程要求系统执行数量比逻辑内核数多的并发线程。 但是,这也可能表示系统中的其他进程正在使用假定可用于此进程的资源。
计算此问题时应注意以下事项:
整个系统可能被过度订阅。 考虑系统中的其他进程可能会抢占您的线程。 当将鼠标指针暂停在线程视图中的抢占段上时,工具提示将指示抢占此线程的线程和进程。 此进程不一定是在您的进程被抢占的整个期间内执行的进程,但它会提示是什么给您的进程创建了抢占压力。
评估您的进程如何确定适合在此工作阶段执行的线程数目。 如果您的进程直接计算活动并行线程的数目,则需考虑修改算法以更好地符合系统上的可用逻辑内核数。 如果使用并发运行时、任务并行库或 PLINQ,则这些库执行计算线程数的工作。
I/O 效率低
过多使用或误用 I/O 是造成应用程序效率低的一个常见原因。 仔细观察上图。 可见时间线分析显示,有 42% 的可见线程时间被 I/O 占用。 时间线显示,存在大量 I/O,这表示被分析的应用程序频繁地被 I/O 阻塞。 若要查看有关各种 I/O 和程序阻塞位置的详细信息,请放大有问题的区域,检查可视时间线配置文件,然后单击特定 I/O 块以查看当时的调用堆栈。
锁保护
当应用程序以先到先服务的顺序获取锁时,以及当锁的到达比率高于获取比率时,就会发生锁保护。 这两种情况组合在一起将导致请求锁启动备份。 有一种方法可防止此问题,那就是使用“不公平”锁,即允许第一个线程查找处于未锁定状态中的线程。 上图显示了此保护行为。 要解决该问题,请尝试减少同步对象的争用,并尝试使用不公平锁。