跳过导航链接 | |
退出打印视图 | |
![]() |
Oracle Solaris Studio 12.3:性能分析器 Oracle Solaris Studio 12.3 Information Library (简体中文) |
每个事件的数据都包含高精度时间戳、线程 ID 和 CPU ID。这些项可用于在性能分析器中按时间、线程或 CPU 过滤度量。有关 CPU ID 的信息,请参见 getcpuid(2) 手册页。在 getcpuid 不可用的系统上,处理器 ID 为 -1(它映射为“未知”)。
除了通用数据外,每个事件还生成特定的原始数据,将在以下各节中对此进行描述。每节还将介绍从原始数据派生的度量的准确性,以及数据收集对度量的影响。
基于时钟的分析的事件特定数据由分析间隔计数的数组组成。在 Oracle Solaris 上,提供了间隔计数器。在分析间隔结束时,相应的间隔计数器加 1,并安排另一个分析信号。仅当 Solaris 线程进入 CPU 用户模式时,才记录和重置数组。重置数组包括将用户 CPU 状态的数组元素设置为 1,将所有其他状态的数组元素设置为 0。在重置数组之前,进入用户模式时会记录数组数据。因此,数组包含自上次进入用户模式以来进入的每个微状态的计数累积(内核为每个 Solaris 线程维护十个微状态)。在 Linux 操作系统上,不存在微状态;唯一的间隔计数器是“用户 CPU 时间”。
在记录数据的同时记录调用堆栈。如果在分析间隔结束时 Solaris 线程未处于用户模式,则在线程再次进入用户模式之前调用堆栈无法更改。因此,调用堆栈总是会在每个分析间隔结束时准确记录程序计数器的位置。
Oracle Solaris 上每个微状态提供的度量显示在 表 6-1 中。
表 6-1 内核微状态如何服务于度量
|
计时数据是基于统计收集的,因此易于出现任何统计抽样方法的所有误差。对于时间非常短的运行(仅记录少量分析数据包),调用堆栈可能不能表示程序中使用大多数资源的各部分。因此应以足够长的时间或足够多的次数运行程序,以累积感兴趣的函数或源代码行的数百个分析数据包。
除了统计抽样误差外,收集和归属数据的方式以及程序在系统中前进方式也会引起特定的误差。以下是计时度量可能出现不准确或失真的一些情况:
创建线程时,记录第一个分析数据包之前所用的时间少于分析间隔,但是整个分析间隔取决于第一个分析数据包中记录的微状态。如果创建了许多线程,则误差可能是分析间隔的许多倍。
销毁线程时,在记录最后一个分析数据包后会消耗一些时间。如果销毁了许多线程,则误差可能是分析间隔的许多倍。
在分析间隔期间可能发生线程的重新调度。因此,记录的线程状态可能不能表示消耗大部分分析间隔的微状态。如果要运行的线程多于要运行它们的处理器,则误差很可能更大。
程序可以与系统时钟相关联的方式运行。在这种情况下,如果线程处于可能表示所用的一小部分时间的状态,而为特定程序部分记录的调用堆栈被过度表示,则分析间隔始终会过期。在多处理器系统上,分析信号可以引入相关性:记录微状态时,在运行程序的线程时由分析信号中断的处理器很可能处于陷阱 CPU 微状态。
分析间隔过期时,内核记录微状态值。如果系统负载过重,则该值可能无法表示进程的真实状态。在 Oracle Solaris 上,此情况很可能会导致对陷阱 CPU 或等待 CPU 微状态的过多记帐。
系统时钟与外部源同步时,在分析数据包中记录的时间戳不反映分析间隔,但包括对时钟进行的任何调整。时钟调整可能使分析数据包看起来已丢失。涉及的时间段通常为几秒,并且调整是以增量方式进行的。
动态更改其操作时钟频率的计算机上所记录的实验可能造成分析不准确。
除刚刚介绍的不准确性外,计时度量还会因收集数据的过程而失真。记录分析数据包所用的时间从不出现在程序的度量中,因为记录是由分析信号启动的。(这是相关性的另一个实例。记录过程中所用的用户 CPU 时间在所记录的任何微状态之间分配。结果是对用户 CPU 时间度量过少记帐,而对其他度量过多记帐。记录数据所用的时间量通常不到缺省分析间隔的 CPU 时间的百分之几。
如果将通过在基于时钟的实验中进行分析所获得的计时度量与通过其他方式获得的时间进行比较,则应该注意以下问题。
对于单线程应用程序,为进程记录的总线程时间通常精确到千分之几(与同一进程的 gethrtime(3C) 返回的值相比)。CPU 时间可能与由同一进程的 gethrvtime(3C) 返回的值相差几个百分点。如果负载过重,则差异可能更加明显。但是,CPU 时间差异并不表示系统失真,并且为不同函数、源代码行等报告的相对时间也不会显著失真。
性能分析器中报告的线程时间可能与 vmstat 报告的时间有很大差异,因为 vmstat 报告的是各 CPU 的线程时间总和。如果目标进程具有的 LWP 比它所运行的系统具有的 CPU 多,则性能分析器显示的等待时间比 vmstat 报告的长。
出现在性能分析器的“统计数据”标签和 er_print 统计显示中的微状态计时基于进程文件系统 /proc 使用报告,因此微状态中所用时间的记录具有很高的准确性。有关更多信息,请参见 proc (4) 手册页。可以将这些计时与 <Total> 函数(它将程序作为一个整体表示)的度量进行比较,以获取聚集计时度量的准确性指示。但是,“统计数据”标签中显示的值可能包含其他基值,而 <Total> 的计时度量值中不包含这些基值。这些基值来自暂停数据收集的时间段。
用户 CPU 时间和硬件计数器循环时间是不同的,因为在将 CPU 模式切换到系统模式时会关闭硬件计数器。有关更多信息,请参见陷阱。
硬件计数器溢出分析数据包括计数器 ID 和溢出值。该值可能大于计数器的溢出设置值,因为处理器在事件的溢出和记录之间会执行某些指令。尤其对循环和指令计数器来说,该值可能会更大,这些计数器的递增频率比诸如浮点运算或高速缓存未命中次数的计数器更快。记录事件中的延迟还意味着,通过调用堆栈记录的程序计数器地址并不精确对应于溢出事件。有关更多信息,请参见硬件计数器溢出的归属。另请参见陷阱的讨论。陷阱和陷阱处理程序可以导致报告的用户 CPU 时间和循环计数器报告的时间有很大差别。
动态更改其操作时钟频率的计算机上所记录的实验在基于周期的时间计数转换时显示不准确性。
收集数据所用的时间可能是程序执行时间的很大一部分。收集运行可能要用大部分时间来处理溢出和写入数据而不是运行程序。
计数的很大一部分可能来自收集过程。这些计数被归属到收集器函数 collector_record_counters。如果看到此函数的计数很大,则表明溢出值过小。
数据的收集可以更改程序的行为。例如,如果要收集有关高速缓存未命中次数的数据,则大多数未命中可能是由刷新收集器指令以及分析高速缓存中的数据并将其替换为程序指令和数据所导致的。程序的高速缓存未命中次数看上去似乎很大,但是如果没有数据收集,则实际上高速缓存未命中次数可能非常小。
数据空间分析是对用于内存引用的硬件计数器分析的扩展。硬件计数器分析可以归属到用户函数、源代码行和指令的度量,但不归属到正在引用的数据对象的度量。缺省情况下,该收集器仅捕获用户指令地址。启用数据空间分析时,该收集器还捕获数据地址。回溯是一些计算机上使用的用于获取支持数据空间分析的性能信息的技术。启用回溯时,收集器回顾在硬件计数器事件发生之前执行的装入或存储指令,以查找可导致该事件的候选指令。在一些系统上,计数器是精确的,无需回溯。在 collect -h 命令的输出中,此类计数器用字 precise 表示。
数据空间分析是一个数据集合,它将针对导致事件的数据对象引用而非仅针对发生内存事件的指令报告内存相关事件(例如高速缓存未命中次数)。
内存空间分析与数据空间分析类似,但在内存空间分析中,针对内存子系统的组件(例如高速缓存行或页面)而非针对程序中的数据对象报告事件。当在与内存有关的精确计数器上前置 + 号时,将发生内存空间分析。
要允许进行数据空间分析,目标必须是使用 -xhwcprof 标志和 -xdebugformat=dwarf -g 标志为 SPARC 体系结构编译的 C、C++ 或 Fortran 程序。此外,收集的数据必须是与内存相关的计数器的硬件计数器分析数据,且必须在计数器名称上前置 + 号。如果在一个(非全部)与内存相关的计数器上前置了可选 +,不带 + 的计数器将针对 <<Unknown> 数据对象报告数据空间数据,其子类型为Dataspace data not requested during data collection。
在具有精确中断的计算机上,不需要回溯,内存空间分析不需要用于编译的 -xhwcprof 标志和 -xdebugformat=dwarf -g 标志。数据空间分析(即使在此类计算机上)不需要标志。
当实验包含数据空间或内存空间分析时,er_print 实用程序允许执行三条附加命令:data_objects、data_single 和 data_layout 以及与内存对象相关的各种命令。有关更多信息,请参见控制硬件计数器数据空间和内存对象列表的命令。
此外,性能分析器包括两个与数据空间分析相关的标签(即“数据对象”标签和“数据布局”标签),以及用于内存对象的各种标签。请参见`DataObjects`(数据对象)标签、`DataLayout`(数据布局)标签和`MemoryObjects`(内存对象)标签。
运行不带其他参数的 collect -h 将列出硬件计数器,并指明这些计数器是否与装入、存储或装入存储相关以及它们是否精确。请参见硬件计数器溢出分析数据。
收集器通过跟踪对线程库 libthread.so 中函数的调用或对实时扩展库 librt.so 的调用来收集同步延迟事件。事件特定的数据由请求和授权的高精度时间戳(跟踪的调用的开始和结束)以及同步对象(例如,请求的互斥锁)的地址组成。线程 ID 和 LWP ID 是记录数据时的 ID。等待时间是请求时间和授权时间之间的差值。仅记录其等待时间超过指定阈值的事件。同步等待跟踪数据在授权时记录在实验中。
等待线程不能执行任何其他工作,直到导致延迟的事件完成为止。等待所用时间同时显示为同步等待时间和用户锁定时间。用户锁定时间可能比同步等待时间长,因为同步延迟阈值筛去了短期延迟。
数据收集的开销使等待时间失真。该开销与收集的事件数成比例。通过增加用于记录事件的阈值,可以最大限度地减少开销中所用的等待时间部分。
收集器通过插入内存分配和解除分配函数 malloc、realloc、memalign 和 free 来记录对这些函数的调用的跟踪数据。如果程序分配内存时忽视这些函数,则不记录跟踪数据。不记录 Java 内存管理(它使用不同的机制)的跟踪数据。
跟踪的函数可能从许多库中的任一个库装入。在性能分析器中看到的数据可能取决于从其装入给定函数的库。
如果程序在很短的时间段内发出对被跟踪函数的大量调用,则执行程序所用的时间可能会大大延长。额外的时间将用于记录跟踪数据。
MPI 跟踪基于修改的 VampirTrace 数据收集器。有关更多信息,请参见 Technische Universität Dresden Web 站点上的 VampirTrace 用户手册。