查看完整版本: 使用适用于 Win32* 线程的英特尔® 线程性能分析器:原理与理论

admin 2007-11-11 18:49

使用适用于 Win32* 线程的英特尔® 线程性能分析器:原理与理论

介绍
作者:Clay P. Breshears
并行应用工程师

过去,软件分析工具侧重于测量应用中函数和过程的执行时间。对于串行应用,这是很有用的信息,可指导程序员关注能从优化或线程化中获得最大优势的那部分代码。但对于已线程的应用,此类信息没有太大意义。

与此相比,更有意义的是与线程化执行和并行执行的性能问题直接相关的信息。有价值的数据可用于降低线程应用的整体执行时间,这些数据包括线程在 OpenMP* 结构末尾的隐含障碍处占用的空闲处理时间,获取对锁定或关键区域访问时的等待时间,关键区域代码上花费的时间,以及针对并行循环执行迭代计划的开销时间等,这里仅举几例介绍。并行区域和串行区域的结构本质、OpenMP 的线程执行模型、严格的 fork-join,这些都有助于通过特尔® 线程档案器轻松测量这些以及其他潜在性能问题。

通过诸如 Win32* 线程等显式线程库编码的算法不具有 OpenMP 所固有的结构化执行。实际上,就线程及其交互而言,设计此类线程化应用程序的方式几乎没有什么限制。即使程序员要模拟 OpenMP 线程执行,但要自动化工具确定程序的结构,这是几乎不可能的任务。因此,需要采用其他方法来分析显式线程化应用程序的性能。这样的新方法已经研发成功,且包含于英特尔线程性能分析器 2.0 中,它可以测量和发现显式线程化代码中的性能问题。

此系列文章共有两部分,本文是第一部分,介绍了适用于 Win32* 线程的英特尔线程分析器的功能,以及如何将其有效地用于显式线程化应用程序。第一部分向读者介绍该工具使用的概念和构想,以及为测量处理器使用率并执行关键路径分析而收集的各种数据。第二部分将通过示例代码具体说明如何使用线程性能分析器工具,显示如何应用这些概念测量线程化应用程序的性能。还将给出使用工具发现和找到线程化代码中典型性能问题的方法。

读者无需熟悉英特尔® 线程处理工具即可理解第一部分中的讨论。但是在第二部分中,我们假定读者熟悉英特尔线程性能分析器。

线程化执行的示例
适用于 Win32 线程的英特尔线程性能分析器可以测量和显示的主要性能度量有两种:(1) 处理资源的使用情况 (2) 其他线程和事件对线程执行的影响的特征。在说明该工具所使用的相关度量和概念之前,我们先演示一个简单示例应用程序,该程序在双处理器系统上使用三个线程。

图 1 显示三个线程的执行时间线。在 E0 时,主处理线程 (T1) 开始执行。在 E1 时,线程 T1 创建另外两个线程(T2 和 T3)。这些线程在 E2 时开始执行。线程 T1 在 E3 时调用 WaitForMultipleObjects,以便等待所创建的线程完成执行。E2 之后和 E4 之前的某个时刻,线程 T2 获得 Lock L,因此线程 T3 在 E4 请求 Lock L 时,它必须等待。在 E5 时,线程 T2 释放 Lock L,在 E6 时,线程 T3 获得对该锁的控制。在 E7 时,线程 T3 被阻塞,等待将在 E8 时完成的某些外部事件。在 E9 时,线程 T2 终止,而在 E10 时,线程 T3 终止。在 E11 时,线程 T1 已收到其等待的两个线程已经完成的信号,因此 T1 继续某些最终计算,然后在 E12 时自行终止。

[img=380,170]http://www.intel.com/cd/ids/developer/apac/zho/332228.htm[/img]

[align=center][url=http://www.intel.com/cd/ids/developer/apac/zho/332228.htm][color=#0860a8]点击查看大图[/color][/url][/align]
[b]图 1. 线程化执行示例[/b]

资源使用情况
根据线程化执行的每个阶段上的并发级别测量处理器使用情况。在适用于 Win32 线程的英特尔线程性能分析器下,将并发级别定义为任意给定时刻的活动线程数量,即正在执行或可用于执行的线程,而不是等待、睡眠或被任何事件或同步请求阻塞的线程。下表中给出了图 1 中的示例应用程序在各个时间线上的并发级别。每个条目对应于两个连续事件之间的一个独立时间片,并以事件对中的第二个事件进行标记。


[table][tr][td][b]结束事件[/b][/td][td][b]并发级别[/b][/td][td][b]结束事件[/b][/td][td][b]并发级别[/b][/td][/tr][tr][td][b]E1[/b][/td][td][b]1[/b][/td][td][b]E7[/b][/td][td][b]2[/b][/td][/tr][tr][td][b]E2[/b][/td][td][b]1[/b][/td][td][b]E8[/b][/td][td][b]1[/b][/td][/tr][tr][td]align="center">[b]E3[/b][/td][td][b]3[/b][/td][td][b]E9[/b][/td][td][b]2[/b][/td][/tr][tr][td][b]E4[/b][/td][td][b]2[/b][/td][td][b]E10[/b][/td][td][b]1[/b][/td][/tr][tr][td][b]E5[/b][/td][td][b]1[/b][/td][td][b]E11[/b][/td][td][b]0[/b][/td][/tr][tr][td][b]E6[/b][/td][td][b]1[/b][/td][td][b]E12[/b][/td][td][b]1[/b][/td][/tr][/table]
表 1. 线程执行的并发级别

适用于 Win32 线程的英特尔线程性能分析器定义了五类并发级别:


[list][*][b]空闲[/b] – 所有线程均不活动,都被阻塞或等待外部事件。[*][b]串行[/b] – 仅一个线程活动。对于应用程序的某些部分,这是必需的(例如,全局数据的启动、关闭、初始化)。串行执行时间超出预期或过多,这表明需要进行串行调整,或并没有有效利用算法的并行处理。[*][b]订阅不足[/b] – 活动线程数低于可用处理器数。如果有较多的活动线程,则处理器资源可以得到更好的利用,应用程序的执行时间减少。此类别中的时间还可能表明线程之间负载不均衡。[*][b]并行[/b] – 活动线程数量等于处理器数量。这是理想情况,因为应用程序利用了全部处理资源。增加应用程序在此类中使用的时间,降低在前一类中使用的时间,这是线程化性能调整的主要目标。[*][b]订阅过多[/b] – 待执行线程数量多于可用处理器数量。尽管并非理想情况,但此情况好于处理器使用不足的情况。但是,在此类中使用的时间表明,应用程序可以使用更少的线程执行,但仍能维持当前性能水平。[/list]
适用于 Win32 线程的英特尔线程性能分析器给出应用程序并发级别的直方图摘要。并进一步使用柱线颜色形象表明基于应用程序执行时所使用的平台架构的并发级别的类别。图 2 显示理想版本的并发级别直方图,它由适用于 Win32 线程的英特尔线程性能分析器针对双处理器系统上的示例应用程序而提供。柱的高度表明每个并发级别上使用的时间。灰色表明所有线程均不活动(空闲类);红色和黄色表明串行和订阅不足的执行;绿色表明完全并行;而订阅过多的时间由蓝色柱表示。

[img]http://www.intel.com/cd/ids/developer/apac/zho/332229.htm[/img]

[b]图 2. 并发级别直方图示例[/b]

对于双处理器系统,如我们的示例,顺序时间是唯一一个订阅不足的类。在四处理器系统上运行可以进一步精细显示订阅不足类,即,两个线程和三个线程。实际上,适用于 Win32 线程的英特尔线程性能分析器将显示在应用程序执行过程中记录的活动线程的每个不同数量,并以一个新柱线显示。每个柱的类别和颜色取决于执行平台上的可用处理器数量。

关键路径分析
适用于 Win32 线程的英特尔线程性能分析器根据应用程序执行过程期间线程运行的时间定义了执行流。流结束于线程等待或终止时。当某线程创建新线程或向另外一个线程发出信号(取消阻塞)时,一个流将分为两个独立的流。这样,在任意给定时刻,在执行内可以有多个流重合,这些流可能会穿过各个线程,因为这些线程之间相互交互,而且线程之间共享同步对象。

对于示例应用程序,有四个明显的流。第一个是线程 T1,从 E0 到 E3。这个流在 E1 时拆分为另外两个流,分别进入线程 T2 和 T3。线程 T3 的流在 E4 终止,此时该线程等待 Lock L。T2 流在 E5 时拆分为两个流。原来的 T2 流继续,直至 E9 时随线程终止而终止。当 T2 释放锁时,该拆分出来的流迁移至线程 T3。该流在线程 T3 上继续,并跨过外部阻塞事件,因为另外一个线程并没有使 T3 暂停。取消阻塞 T1 的线程 T3 一终止,该流立即连接线程 T1。当 T1 终止时,该流最终结束。

在适用于 Win32 线程的英特尔线程性能分析器中,最长的执行流即所谓的“关键路径”。示例应用程序中的关键路径是这样的流,即起始于线程 T1,在 E2 时转移至线程 T2,在 E6 时转移至线程 T3,然后在 E11 时返回线程 T1。它不同于英特尔® VTune™ 性能分析器中所使用的“调用关系图”分析中所使用的“关键路径”术语。在“调用关系图”分析中,关键路径是调用关系图树中占用最多执行时间的路径(分支集合)。

在应用程序停止执行之前,适用于 Win32 线程的英特尔线程性能分析器无法确定给定执行的关键路径。在 E5 和 E9 之间,有两个路径都是潜在的候选关键路径。如果线程 T3 在线程 T2 之前终止,则穿过 T2 的执行流将成为关键路径。当无潜在候选关键路径时,将放弃适用于 Win32 线程的英特尔线程性能分析器所收集的全部数据集合。这样做的目的在于使收集和保存的数据量达到最小。

如果关键路径的执行时间可以缩短,则整个应用程序执行时间将缩短。通过将数据收集集中于关键路径,适用于 Win32 线程的英特尔线程性能分析器能够看到哪些线程位于关键路径上,哪些对象导致关键路径转移至其他线程。因此,该工具能够发现阻塞其他线程运行的线程以及阻塞这些线程所使用的对象。

除关键路径外,适用于 Win32 线程的英特尔线程性能分析器还定义了以下四类线程交互。


[list][*][b]畅行时间[/b] - 线程不会推迟关键路径中下一线程执行的时间。[*][b]开销时间[/b] - 线程同步或操作系统计划开销。[*][b]阻塞时间[/b] - 线程在关键路径上等待外部事件或阻塞时所花费的时间(包括超时时间)。[*][b]影响时间[/b] - 关键路径上的线程推迟下一线程进入关键路径的时间。[/list]
对于示例应用程序,从线程 T1 创建线程 T2 时开始的关键路径转移时间(E1 至 E2)以及线程 T3 终止和线程 T1 恢复之间的时间(E10 至 E11)都将计为开销时间。在 E7 和 E8 之间内,线程 T3 被阻塞等待外部事件,这段时间将视为阻塞时间。由于线程 T2 正在使用 Lock L,直接阻止线程 T3 继续,关键路径上 E4 和 E5 之间的部分是影响时间。

关键路径上 E2 和 E4 之间的时间是畅行时间。尽管由于线程 T2 和 T3 的持续执行直接影响并导致线程 T1 的阻塞,但由于在该阶段执行内,关键路径上并没有任何线程阻塞其他线程的执行,所以该段时间仍视为畅行时间。E9 和 E10 之间的时间是影响时间,因为当时线程 T3 是关键路径上唯一阻止线程 T1 继续执行的线程。

适用于 Win32 线程的英特尔线程性能分析器使用直方图摘要显示关键路径上的时间分类。该数据在并发级别直方图内堆叠起来,并使用不同颜色亮度表示。使用比较明亮的颜色表示影响时间,而使用较淡的阴影表示畅行时间。图 3 显示了具有关键路径分类数据的理想化并发直方图。明亮的黄色表示没有任何活动线程时(当 T3 在 E10 至 E11 之间终止时)以及关键路径上仅有单个活动线程时(创建 T2、T3 时和 T2 释放 Lock L 后)的开销。多数并行时间(2 个线程)是畅行时间(淡绿色)。只有少量时间内(E8 和 E9 之间的 T3),已知一个线程直接影响另外一个线程 (T1) 的执行,该段时间以亮绿色显示。

[img]http://www.intel.com/cd/ids/developer/apac/zho/332227.htm[/img]

[b]图 3. 并发直方图内的关键路径数据[/b]
摘要
本文展示了适用于 Win32 线程的英特尔线程性能分析器在测量显式线程化应用程序性能时所使用的两个主要概念和度量。[b]并发级别[/b]是对线程化代码运行过程中系统处理资源利用情况的测量。最完美的实现是,活动执行的线程数量等于可用处理器的数量。如果线程少于处理器,则会产生空闲处理周期,线程多于处理器将使处理器始终繁忙。资源的订阅过多表明使用更少的线程也可以获得相同的性能。

[b]关键路径分析[/b]显示了执行过程中线程之间的相互交互方式。该分析关注于执行流,其性能特征将对整体性能有最大影响。程序员在调整代码时应该关注关键路径。在一个线程程阻塞其他线程的执行或外部事件阻塞线程的位置重新组织计算,这可以产生较好的性能。

在第二部分中,我们将研究本文中所阐述的构想如何在适用于 Win32 线程的英特尔线程性能分析器中实现,以及如何使用该工具中显示的信息指明并发现线程化代码中的性能问题。
页: [1]
查看完整版本: 使用适用于 Win32* 线程的英特尔® 线程性能分析器:原理与理论