可通过按固定的间隔记录分析事件来收集分析数据。 该间隔可以是使用系统时钟获取的时间间隔,也可以是特定类型硬件事件的数目。间隔时间结束时,会向系统传送一个信号,并在下一个间隔记录数据。
可通过在各种系统函数和库函数上插入包装函数来收集跟踪数据,以便拦截对函数的调用,并记录有关调用的数据。
可通过调用各种系统例程获取全局信息来收集抽样数据。
通过检测可执行文件和任何共享对象,为可执行文件以及动态打开或静态链接到可执行文件并经过检测的任何共享对象收集函数和指令计数数据。每个函数或基本块的执行次数会记录下来。
收集线程分析数据以支持线程分析器。
分析数据和跟踪数据都包含有关特定事件的信息,并且这两种类型的数据都会转换为性能度量。抽样数据不会转换为度量,而是用于提供标记,这些标记可用于将程序执行划分为很多时间段。通过抽样数据,可以了解该时间段内程序执行的总体情况。
每个分析事件或跟踪事件收集的数据包都包含以下信息:
标识数据的数据包头。
高精度的时间戳。
线程 ID。
处理器 (CPU) ID,在操作系统中可用时。
调用堆栈的副本。对于 Java 程序,将记录两个调用堆栈:计算机调用堆栈和 Java 调用堆栈。
对于 OpenMP 程序,还会收集当前并行区域的标识符和 OpenMP 状态。
有关线程和轻量级进程的更多信息,请参见了解性能分析器及其数据。
除了通用数据外,每个事件特定的数据包还包含特定于数据类型的信息。
进行时钟分析时,收集的数据取决于操作系统所提供的信息。
在 Oracle Solaris 下的时钟分析中,每个线程的状态都按固定的时间间隔存储。 该时间间隔称为分析间隔。使用分析间隔的精度将收集的数据转换为每个状态所用的时间。
缺省分析间隔约为 10 毫秒 (10 ms)。可以指定高精度分析间隔(大约为 1 毫秒)和低精度分析间隔(大约为 100 毫秒)。如果操作系统允许,也可以指定定制间隔。不带任何其他参数运行 collect -h 可输出系统所允许的范围和精度。
下表显示当实验包含时钟分析数据时,性能分析器和 er_print 可显示的性能度量。请注意,来自所有线程的度量加在了一起。
| 
 | 
计时度量按多种类别说明程序消耗时间的位置,并且可用于改善程序的性能。
高用户 CPU 时间说明程序处理大部分工作的位置。可使用它来查找重新设计算法后可能受益最多的程序部分。
高系统 CPU 时间说明程序在对系统例程的调用中消耗了大量时间。
高等待 CPU 时间说明准备运行的线程数比可用的 CPU 多,或其他进程正在使用 CPU。
高用户锁定时间说明线程无法获得其请求的锁定。
高文本缺页时间意味着链接程序要求的代码会在内存中进行组织,所以很多调用或分支会导致装入新的页面。
高数据缺页时间表明对数据的访问会导致新的页面被装入。重新组织程序的数据结构或算法可以修复此问题。
在 Linux 平台上,只能将时钟数据显示为 CPU 总时间。Linux CPU 时间是用户 CPU 时间和系统 CPU 时间的总和。
如果在 OpenMP 程序上执行时钟分析,还提供其他度量:Master Thread Time(主线程时间)、OpenMP Work Time(OpenMP 工作时间)和 OpenMP Wait Time(OpenMP 等待时间)。
在 Oracle Solaris 上,"Master Thread Time"(主线程时间)是主线程消耗的总时间,它与挂钟时间相对应。该度量在 Linux 上不可用。
在 Oracle Solaris 上,以串行或并行方式执行工作时,"OpenMP Work Time"(OpenMP 工作时间)会累计。在以下情况下,"OpenMP Wait Time"(OpenMP 等待时间)会累计:OpenMP 运行时正在等待进行同步时、该等待正在使用 CPU 时间或正在休眠时,以及正在以并行方式执行工作,但未在 CPU 上安排线程时。
在 Linux 操作系统上,仅当进程在用户模式或系统模式下处于活动状态时,"OpenMP Work Time"(OpenMP 工作时间)和 "OpenMP Wait Time"(OpenMP 等待时间)才会累计。除非您已指定 OpenMP 应执行忙等待,否则,Linux 上的 "OpenMP Wait Time"(OpenMP 等待时间)没有用处。
OpenMP 程序的数据可以在三种查看模式中的任一种模式下显示。在用户模式下,从线程按实际从主线程克隆的方式显示,并使其调用堆栈匹配主线程的调用堆栈。调用堆栈中来自 OpenMP 运行时代码 (libmtsk.so) 的帧会被禁止。在专家用户模式下,主从线程的显示方式不同,编译器生成的显式函数可见,来自 OpenMP 运行时代码 (libmtsk.so) 的框架将被禁止。对于计算机模式,将显示实际的本机堆栈。
er_kernel 实用程序可以收集有关 Oracle Solaris 内核的基于时钟的分析数据。要分析内核,可从命令行直接运行 er_kernel 实用程序,或从性能分析器的 "File"(文件)菜单中选择 "Profile Kernel"(分析内核)。
er_kernel 实用程序捕获内核分析数据,并将数据记录为性能分析器实验,其格式与 collect 实用程序在用户程序上创建的实验格式相同。实验可以由 er_print 实用程序或性能分析器进行处理。内核实验可以显示函数数据、调用方-被调用方数据、指令级数据和时间线,但是不能显示源代码行数据(因为大多数 Oracle Solaris 模块不包含行号表)。
er_kernel 还可以对正在运行的用户有权限的任何进程记录用户级实验。此类实验类似于 collect 创建的实验,但它只包含 "User CPU Time"(用户 CPU 时间)和 "System CPU Time"(系统 CPU 时间)数据,不支持 Java 或 OpenMP 分析。
有关更多信息,请参见内核分析。
可以在用 Oracle Message Passing Toolkit(以前称为 Sun HPC ClusterTools)运行的 MPI 实验中收集时钟分析数据。Oracle Message Passing Toolkit 必须至少为版本 8.1。
Oracle Message Passing Toolkit 已集成在 Oracle Solaris 11 发行版中。如果系统中安装了此工具包,您可以在 /usr/openmpi 中找到它。如果您的 Oracle Solaris 11 系统中尚未安装此工具包,当您为系统配置了软件包系统信息库时,可以使用命令 pkg search openmpi 搜索该工具包。有关在 Oracle Solaris 11 中安装软件的更多信息,请参见《在 Oracle Solaris 11 中添加和更新软件》。
在 MPI 实验中收集时钟分析数据时,还可以查看两个其他度量:
MPI Work Time(MPI 工作时间),该度量将在进程正在 MPI 运行时内执行工作(如处理请求或消息)时累计。
MPI Wait Time(MPI 等待时间),该度量将在进程正在 MPI 运行时内等待事件、缓冲区或消息时累计
在 Oracle Solaris 上,以串行或并行方式执行工作时,MPI 工作时间会累计。在以下情况下,MPI 等待时间会累计:MPI 运行时正在等待进行同步时、该等待正在使用 CPU 时间或正在休眠时,以及正在以并行方式执行工作,但未在 CPU 上调度线程时。
在 Linux 上,仅当进程在用户模式或系统模式下处于活动状态时,MPI 工作时间和 MPI 等待时间才会累计。除非您已指定 MPI 应执行忙等待,否则,Linux 上的 MPI 等待时间没有用处。
Oracle Message Passing Toolkit 版本号由安装路径指定,例如 /opt/SUNWhpc/HPC8.2.1,或者,您可以按照如下所示键入 mpirun —V 查看输出,其中版本以斜体表示:
mpirun (Open MPI) 1.3.4r22104-ct8.2.1-b09d-r70
如果您的应用程序是使用 GNU 或 Intel 编译器编译的,并且要对 MPI 使用 Oracle Message Passing Toolkit 8.2 或 8.2.1,则要获取 MPI 状态数据,必须使用 –WI 和 –-enable-new-dtags 选项和 Oracle Message Passing Toolkit link 命令。这些选项将使可执行文件定义 RPATH 及 RUNPATH,从而可使用 LD_LIBRARY_PATH 环境变量启用 MPI 状态库。
硬件计数器可跟踪诸如高速缓存未命中次数、高速缓存停止周期、浮点运算、分支误预测、CPU 周期以及执行指令之类的事件。在硬件计数器分析中,当运行线程的 CPU 的指定硬件计数器发生溢出时,收集器将记录一个分析数据包。 计数器将重置并继续进行计数。分析数据包中包括溢出值和计数器类型。
各种处理器芯片系列支持同时存在二到十八个硬件计数器寄存器。收集器可收集一个或多个寄存器上的数据。对于每个寄存器,可以选择监视溢出的计数器的类型,并为计数器设置溢出值。 有些硬件计数器可以使用任意寄存器,而有些计数器仅可以使用特定的寄存器。因此,在一个实验中并非可以选择所有的硬件计数器组合。
硬件计数器分析还可以在内核上执行:通过性能分析器和 er_kernel 实用程序执行。有关更多信息,请参见内核分析。
硬件计数器分析数据由性能分析器转换为计数度量。对于以循环方式计数的计数器,所报告的度量会转换为次数;而对于不以循环方式计数的计数器,所报告的度量为事件计数。在具有多个 CPU 的计算机上,用于转换度量的时钟频率为各个 CPU 时钟频率的调和平均数。因为每种类型的处理器都有其自己的一组硬件计数器,并且硬件计数器的数目庞大,因此,此处未列出硬件计数器的度量。硬件计数器列表 讲述如何找出可用的硬件计数器。
如果收集两个特定的计数器 "cycles" 和 "insts",则可以使用两个额外的度量,即 "CPI" 和 "IPC",分别表示每指令周期数和每周期指令数。它们始终以比率(而非时间、计数或百分比)的形式显示。高 CPI 值或低 IPC 值指示代码在计算机中运行效率低;相反,低 CPI 值或高 IPC 值指示代码在管道中运行效率高。
硬件计数器的一个用途是可诊断进出 CPU 的信息流问题。例如,高速缓存未命中次数计数较高表明,重新组织程序的结构来改进数据或文本的位置或提高高速缓存的重用率可以改善程序性能。
某些硬件计数器与其他计数器相互关联。例如,分支误预测和指令高速缓存未命中次数通常是相关的,因为分支误预测会导致将错误的指令装入到指令高速缓存中。这些指令必须替换为正确的指令。这种替换会导致指令高速缓存未命中,或指令转换后备缓冲器 (instruction translation lookaside buffer, ITLB) 未命中,或甚至缺页。
对于许多硬件计数器,经常会用导致溢出事件的指令之后的一条或多条指令报告溢出。此类行为称为“失控 (skid)”,它会使得计数器溢出分析很难解释:性能数据会与在实际导致事件发生的指令后执行的指令关联起来。对许多芯片和计数器来说,失控的指令数都是不确定的;但是某些芯片和计数器可以精确确定此类指令数(通常是零个或一个)。
某些内存相关计数器具有一个确定性的失控,并可在执行内存空间分析时使用。它们在硬件计数器列表中所述的列表中标记为 "precise load-store" 计数器。对此类计数器来说,缺省情况下可以确定 PC 和 EA 触发情况并捕获内存空间/数据空间数据。有关更多信息,请参见数据空间分析和内存空间分析。
由于硬件计数器是特定于处理器的,因此可以选用的计数器取决于所使用的处理器。性能工具为许多可能常用的计数器提供了别名。 您可以确定用于分析当前计算机的最大硬件计数器定义数量,并在当前计算机上运行不带任何其他参数的 collect -h,以查看可用硬件计数器及缺省计数器集的完整列表。
如果处理器和系统支持硬件计数器分析,则 collect -h 命令会输出两个包含有关硬件计数器信息的列表。第一个列表包含硬件计数器别名(这些别名是常用名称)。第二个列表包含原始硬件计数器。如果性能计数器子系统和 collect 命令都没有特定系统上的计数器的名称,则这些列表将为空。但是,在大多数情况下,可以用数值指定计数器。
以下示例显示计数器列表中的条目。有别名的计数器将首先显示在列表中,然后是原始硬件计数器列表。该示例中的每一行输出都按打印格式显示。
    Aliases for most useful HW counters:
    alias      raw name                     type      units regs description
    cycles     Cycles_user                       CPU-cycles 0123 CPU Cycles
    insts      Instr_all                             events 0123 Instructions Executed
    c_stalls   Commit_0_cyc                      CPU-cycles 0123 Stall Cycles
    loads      Instr_ld       precise load-store     events 0123 Load Instructions
    stores     Instr_st       precise load-store     events 0123 Store Instructions
    dcm        DC_miss_commit precise load-store     events 0123 L1 D-cache Misses
...
Raw HW counters:
    name                                    type      units regs description
    Sel_pipe_drain_cyc                           CPU-cycles 0123 
    Sel_0_wait_cyc                               CPU-cycles 0123 
    Sel_0_ready_cyc                              CPU-cycles 0123 
...
                    在有别名的硬件计数器列表中,第一个字段(例如,cycles)提供可以在 collect 命令的 -h counter... 参数中使用的别名。此别名还是在 er_print 命令中使用的标识符。
第二个字段提供了计数器的原始名称。例如 loads 是 Instr_ld 的简称
第三个字段含有类型信息,但有可能是空的(例如 precise load-store)。
类型信息字段中的可能条目包括下列项:
precise,此时基于内存的计数器中断与已知的精确失控一起发生,而且支持用于内存空间分析。对于此类计数器,性能分析器可以(而且在缺省情况下也会)收集内存空间和数据空间数据。有关详细信息,请参见"MemoryObjects"(内存对象)视图中的说明。
load、store 或 load-store,表明计数器与内存相关。
not-program-related,计数器会捕获由其他某个程序启动的事件,例如 CPU 到 CPU 的高速缓存嗅探。使用计数器进行分析时将生成警告,并且分析不记录调用堆栈。
第四个字段含有正在计数的单位类型(例如 events)。
该单位可以是以下单位之一:
CPU-cycles,则计数器可用于提供基于时间的度量。针对此类计数器报告的度量在缺省情况下会转换为独占时间和非独占时间,但是也可以显示为事件计数。
events,则度量是非独占和独占事件计数,且无法转换为时间。
第五个字段列出计数器的可用寄存器。例如,0123。
第六个字段简要说明了计数器,例如 Load Instructions。
原始硬件计数器列表中包含的信息是有别名的硬件计数器列表中信息的子集。 原始硬件计数器列表中的每行包括由 cputrack(1) 使用的内部计数器名称、类型信息、计数器单位(可以是 CPU-cycles 或 events)以及在其上使用计数器的寄存器编号。
如果计数器度量与运行的程序无关的事件,则类型信息的第一个单词是 not-program-related。对于这样的计数器,分析不会记录调用堆栈,而是显示人工函数 collector_not_program_related 中所用的时间。会记录线程 ID 和 LWP ID,但没有任何意义。
原始计数器的缺省溢出值为 1000003。对于大多数原始计数器来说,此值并非理想值,所以您应在指定原始计数器时指定溢出值。
在多线程程序中,同步不同线程执行的任务会导致程序执行延迟。例如,一个线程要访问被其他线程锁定的数据时就不得不等待。这些事件称为同步延迟事件,并通过跟踪对 Solaris 或 pthread 线程函数的调用来收集这些事件。 收集和记录这些事件的过程称为同步等待跟踪。 等待锁时花费的时间称为同步等待时间。
只有等待时间超过阈值(单位为微秒)时,才会记录事件。 阈值为 0 表示跟踪所有的同步延迟事件,而不管等待时间为何。缺省阈值通过运行校准测试确定,在该测试中对线程库的调用不会出现任何同步延迟。 阈值是这些调用的平均时间与某个因子(当前为 6)的乘积。该过程可防止对此类事件进行记录:即等待时间仅在于调用本身,而与实际的延迟无关。因此,数据量会大大减少,但同步事件的计数可能会被明显低估。
对于 Java 程序,同步跟踪可能会涵盖已分析程序中的 Java 方法调用、本机同步调用或同时涵盖这两种调用。
| 
 | 
通过该信息,您可以确定函数或装入对象对同步例程进行调用时是会经常被阻塞还是会经历很长时间的等待。高同步等待时间表示线程间的争用。您可以通过重新设计算法,尤其是重新组织锁的结构,以便仅包含需要锁定的每个线程的数据来减少争用。
对未正确管理的内存分配和取消分配函数进行调用可能会造成数据的使用效率降低,从而导致应用程序的性能降低。在堆跟踪中,收集器通过插入 C 标准库内存分配函数 malloc、realloc、valloc 和 memalign 以及取消分配函数 free 跟踪内存分配和取消分配请求。对 mmap 的调用被视为内存分配,它允许记录 Java 内存分配的堆跟踪事件。Fortran 函数 allocate 和 deallocate 调用 C 标准库函数,因此会间接跟踪这些例程。
不支持对 Java 程序的堆分析。
| 
 | 
收集堆跟踪数据有助于识别程序中的内存泄漏,或定位内存分配效率不高的位置。
查看应用了过滤器的 "Leaks"(泄漏)视图时,显示的泄漏是在过滤标准下完成的内存分配,这些分配在任何时候都不会取消。泄漏不限于在过滤标准下未取消分配的分配。
内存泄漏在此处的定义是:动态分配的但从未释放的内存块,不管进程地址空间中是否存在指向该内存块的指针。(其他工具可能以不同方式定义泄漏,例如将其定义为:已分配但未释放的内存块,但进程地址空间中不再有指向它的指针。)
I/O 数据收集跟踪输入/输出系统调用,包括读取和写入。该收集测量调用的持续时间,跟踪文件和描述符以及传输的数据量。您可以使用 I/O 度量识别具有较高传输字节量和较长总线程时间的文件、文件句柄和调用堆栈。
| 
 | 
全局数据由收集器中称为抽样包的数据包来记录。 每个数据包中都包含一个包头、时间戳、内核的执行统计信息(如缺页和 I/O 数据)、上下文切换以及各种页面驻留(工作集和分页)统计信息。记录在抽样包中的数据对程序来说是全局的,且不会转换为性能度量。记录抽样包的过程称为抽样。
当程序在 dbx 中调试期间因任何原因停止时(如在断点处,前提是设置了有关在断点处停止的选项)。
使用 dbx collector sample record 命令手动记录抽样时。
如果代码中包含对 collector_sample 的调用,则在调用该例程时(请参见 使用 libcollector 库从程序控制数据收集)。
传送指定信号时(如果将 -l 选项与 collect 命令一起使用,请参见 collect(1) 手册页)。
开始和终止收集时。
使用 dbx collector pause 命令暂停收集时(就在暂停之前)和使用 dbx collector resume 命令恢复收集时(就在恢复之后)。
创建子孙进程前后。
性能工具使用记录在抽样包中的数据按时间段将数据分组,这些称为抽样。 您可以通过选择一组抽样过滤特定于事件的数据,以便只查看这些特定时间段的信息。您也可以查看每个抽样的全局数据。
性能工具不对不同种类的抽样点进行区分。要利用抽样点进行分析,您应只选择一种类型的点进行记录。具体地说,如果要记录与程序结构或执行序列有关的抽样点,则应关闭定期抽样,并使用在 dbx 停止进程时,或将信号传送到正使用 collect 命令记录数据的进程时,或调用收集器 API 函数时记录的抽样。
收集器可以收集有关对消息传递接口 (Message Passing Interface, MPI) 库的调用的数据。
使用开源 VampirTrace 5.5.3 发行版来实现 MPI 跟踪。该跟踪可识别以下 VampirTrace 环境变量:
| 
 | 
有关这些变量的更多信息,请参见 Technische Universität Dresden 网站上的 Vampirtrace 用户手册。
在达到缓冲区限制之后发生的 MPI 事件将不会写入跟踪文件,这将导致跟踪不完整。
要去掉限制并获取应用程序的完整跟踪,请将 VT_MAX_FLUSHES 环境变量设置为 0。该设置将导致 MPI API 跟踪收集器在缓冲区已满时刷新磁盘的缓冲区。
要更改缓冲区大小,请设置 VT_BUFFER_SIZE 环境变量。该变量的最佳值取决于要跟踪的应用程序。设置较小的值将增加应用程序可以使用的内存,但是将触发 MPI API 跟踪收集器频繁进行缓冲区刷新。这些缓冲区刷新可能会显著改变应用程序的行为。另一方面,设置较大的值(如 2 G)可以使 MPI API 跟踪收集器刷新缓冲区的次数降至最低,但是将减少应用程序可以使用的内存。如果没有足够的内存可用来容纳缓冲区和应用程序数据,应用程序的某些部分可能会交换至磁盘,从而导致应用程序的行为发生显著改变。
| 
 | 
| 
 | 
"MPI Time"(MPI 时间)是 MPI 函数中所用的总线程时间。如果还收集了 MPI 状态时间,则除 MPI_Init 和 MPI_Finalize 之外的所有 MPI 函数的 MPI 工作时间加上 MPI 等待时间应大约等于 MPI 工作时间。在 Linux 上,MPI 等待和工作时间基于用户 CPU 时间加系统 CPU 时间,而 MPI 时间基于实际时间,所以这些数值将不匹配。
当前,仅针对点对点消息收集 MPI 字节和消息计数。不针对集合通信函数记录 MPI 字节和消息计数。"MPI Bytes Received"(接收的 MPI 字节数)度量会计算所有消息中接收的实际字节数。"MPI Bytes Sent"(发送的 MPI 字节数)会计算所有消息中发送的实际字节数。"MPI Sends"(MPI 发送次数)会计算发送的消息数,"MPI Receives"(MPI 接收次数)会计算接收的消息数。
收集 MPI 跟踪数据有助于标识 MPI 程序中可能因 MPI 调用而产生性能问题的位置。可能发生的性能问题的例子有负载平衡、同步延迟和通信瓶颈。