Sun Studio 12:性能分析器

解释性能度量

每个事件的数据都包含高精度时间戳、线程 ID、LWP ID 和处理器 ID。其中的前三项可以用于在性能分析器中按时间、线程或 LWP 过滤度量。有关处理器 ID 的信息,请参见 getcpuid(2) 手册页。在 getcpuid 不可用的系统上,处理器 ID 为 -1(它映射为“未知”)。

除了通用数据外,每个事件还生成特定的原始数据,将在以下各节中对此进行描述。每节还将介绍从原始数据派生的度量的准确性,以及数据收集对度量的影响。

基于时钟的分析

基于时钟的分析的事件特定数据由分析间隔计数的数组组成。在 Solaris OS 上,提供了间隔计数器。在分析间隔结束时,相应的间隔计数器加 1,并安排另一个分析信号。仅当 Solaris LWP 线程进入 CPU 用户模式时,才记录和重置数组。重置数组包括将用户 CPU 状态的数组元素设置为 1,将所有其他状态的数组元素设置为 0。在重置数组之前,进入用户模式时会记录数组数据。因此,数组包含在自上次进入用户模式以来进入的每个微态的计数累积(内核为每个 Solaris LWP 维护十个微态)。在 Linux OS 上,不存在微态;唯一的间隔计数器是“用户 CPU 时间”。

在记录数据的同时记录调用栈。如果在分析间隔结束时 Solaris LWP 未处于用户模式,则在 LWP 或线程再次进入用户模式之前调用栈无法更改。因此,调用栈总是会在每个分析间隔结束时准确记录程序计数器的位置。

Solaris OS 上每个微态所服务于的度量表 7–2 所示。

表 7–2 内核微态如何服务于度量

内核微态 

说明 

度量名称 

LMS_USER

在用户模式下运行 

用户 CPU 时间 

LMS_SYSTEM

在系统调用或缺页时运行 

系统 CPU 时间 

LMS_TRAP

在出现任何其他陷阱时运行 

系统 CPU 时间 

LMS_TFAULT

在用户文本缺页时休眠 

文本缺页时间 

LMS_DFAULT

在用户数据缺页时休眠 

数据缺页时间 

LMS_KFAULT

在内核缺页时休眠 

其他等待时间 

LMS_USER_LOCK

等待用户模式锁定时休眠 

用户锁定时间 

LMS_SLEEP

由于任何其他原因而休眠 

其他等待时间 

LMS_STOPPED

已停止(/proc、作业控制或 lwp_stop

其他等待时间 

LMS_WAIT_CPU

等待 CPU 

等待 CPU 时间 

计时度量的准确性

计时数据是基于统计收集的,因此易于出现任何统计抽样方法的所有误差。对于时间非常短的运行(仅记录少量分析数据包),调用栈可能不能表示程序中使用大多数资源的各部分。因此应以足够长的时间或足够多的次数运行程序,以累积感兴趣的函数或源代码行的数百个分析数据包。

除了统计抽样误差外,收集和归属数据的方式以及程序在系统中前进方式也会引起特定的误差。以下是计时度量可能出现不准确或失真的一些情况:

除了刚刚介绍的不准确外,计时度量还会因收集数据的过程而失真。记录分析数据包所用的时间从不出现在程序的度量中,因为记录是由分析信号启动的。(这是相关性的另一个实例。)记录过程中所用的用户 CPU 时间在所记录的任何微态之间分配。结果是对用户 CPU 时间度量过少记帐,而对其他度量过多记帐。记录数据所用的时间量通常不到缺省分析间隔的 CPU 时间的百分之几。

计时度量的比较

如果将通过在基于时钟的实验中进行分析所获得的计时度量与通过其他方式获得的时间进行比较,则应该注意以下问题。

对于单线程应用程序,为进程记录的 Solaris LWP 或 Linux 线程时间总计通常精确到千分之几(与同一进程的 gethrtime(3C) 返回的值相比)。CPU 时间可能与由同一进程的 gethrvtime(3C) 返回的值相差几个百分点。如果负载过重,则差异可能更加明显。但是,CPU 时间差异并不表示系统失真,并且为不同函数、源代码行等报告的相对时间也不会显著失真。

对于 Solaris OS 上使用未绑定线程的多线程应用程序,gethrvtime() 所返回的值的差异可能没有意义,因为 gethrvtime() 返回 LWP 的值,而线程可能随 LWP 的不同而不同。

性能分析器中报告的 LWP 时间可能与 vmstat 报告的时间有很大差异,因为 vmstat 报告 CPU 的汇总时间。如果目标进程具有的 LWP 比它所运行的系统具有的 CPU 多,则性能分析器显示的等待时间比 vmstat 报告的长。

出现在性能分析器的“统计”标签和 er_print 统计显示中的微态计时基于进程文件系统 /proc 使用报告,因此微态中所用时间的记录具有很高的准确性。有关更多信息,请参见 proc (4) 手册页。可以将这些计时与 <Total> 函数(它将程序作为一个整体表示)的度量进行比较,以获取聚集计时度量的准确性指示。但是,“统计”标签中显示的值可能包含其他基值,而 <Total> 的计时度量值中不包含这些基值。这些基值来自暂停数据收集的时间段。

用户 CPU 时间和硬件计数器循环时间是不同的,因为在将 CPU 模式切换到系统模式时会关闭硬件计数器。有关更多信息,请参见陷阱

同步等待跟踪

同步等待跟踪仅在 Solaris 平台上可用。收集器通过跟踪对线程库 libthread.so 中函数的调用或对实时扩展库 librt.so 的调用来收集同步延迟事件。事件特定的数据由请求和授权的高精度时间戳(跟踪的调用的开始和结束)以及同步对象(例如,请求的互斥锁)的地址组成。线程 ID 和 LWP ID 是记录数据时的 ID。等待时间是请求时间和授权时间之间的差值。仅记录其等待时间超过指定阈值的事件。同步等待跟踪数据在授权时记录在实验中。

在完成导致延迟的事件之前,在其上安排有等待线程的 LWP 无法执行任何其他工作。等待所用时间同时显示为同步等待时间和用户锁定时间。用户锁定时间可能比同步等待时间长,因为同步延迟阈值筛去了短期延迟。

数据收集的开销使等待时间失真。该开销与收集的事件数成比例。通过增加用于记录事件的阈值,可以最大限度地减少开销中所用的等待时间部分。

硬件计数器溢出分析

硬件计数器溢出分析数据包括计数器 ID 和溢出值。该值可能大于计数器的溢出设置值,因为处理器在事件的溢出和记录之间执行某些指令。尤其对循环和指令计数器来说,该值可能会更大,这些计数器的递增频率比诸如浮点运算或高速缓存未命中次数的计数器更快。记录事件中的延迟还意味着,通过调用栈记录的程序计数器地址并不精确对应于溢出事件。有关更多信息,请参见硬件计数器溢出的归属。另请参见陷阱的讨论。陷阱和陷阱处理程序可以导致报告的用户 CPU 时间和循环计数器报告的时间有很大差别。

收集的数据量取决于溢出值。选择过小的值可能会产生以下结果。

选择过大的值可能导致溢出过少,不利于统计。最后一次溢出后产生的计数被归属到收集器函数 collector_final_counters。如果在此函数中看到一大部分计数,则表明溢出值过大。

堆跟踪

收集器通过插入内存分配和解除分配函数 mallocreallocmemalignfree,来记录对这些函数的调用的跟踪数据。如果程序分配内存时忽视这些函数,则不记录跟踪数据。不记录 Java 内存管理(它使用不同的机制)的跟踪数据。

跟踪的函数可能从许多库中的任一个库装入。在性能分析器中看到的数据可能取决于从其装入给定函数的库。

如果程序在很短的时间段内发出对被跟踪函数的大量调用,则执行程序所用的时间可能会大大延长。额外的时间将用于记录跟踪数据。

数据空间分析

数据空间分析是一个数据集合,在其中根据导致事件的数据对象引用(而不仅仅是发生与内存相关事件的指令)报告与内存相关的事件(如高速缓存未命中)。数据空间分析在 Linux 系统上不可用。

要允许进行数据空间分析,目标必须是使用 -xhwcprof 标志和 -xdebugformat=dwarf -g 标志为 SPARC 体系结构编译的 C 程序。此外,收集的数据必须是与内存相关的计数器的硬件计数器分析数据,且必须在计数器名称之前放置可选的 + 号。性能分析器包括两个与数据空间分析相关的标签(即 DataObject 标签和 DataLayout 标签),以及用于内存对象的各种标签。

也可以通过在分析间隔之前放置加号 ( + ),使用时钟分析进行数据空间分析。

MPI 跟踪

MPI 跟踪仅在 Solaris 平台上可用。MPI 跟踪记录有关对 MPI 库函数的调用的信息。事件特定的数据由请求和授权的高精度时间戳(跟踪的调用的开始和结束)、发送和接收的操作数以及发送或接收的字节数组成。跟踪是通过插入对 MPI 库的调用进行的。插入函数既不具有有关数据传输优化的详细信息,也不具有有关传输误差的详细信息,因此提供的信息表示数据传输的简单模型,将在下面段落中对此进行说明。

接收的字节数是指对 MPI 函数的调用中定义的缓冲区长度。接收的实际字节数对于插入函数是不可用的。

有些全局通信函数具有单一起始点或单一接收进程(称为根)。对这样的函数进行记帐的过程如下:

以下示例说明了记帐过程。在这些示例中,G 是组的大小。

对于对 MPI_Bcast() 的调用,

对于对 MPI_Allreduce() 的调用,

对于对 MPI_Reduce_scatter() 的调用,