跳过导航链接 | |
退出打印视图 | |
![]() |
Oracle Solaris Studio 12.3:性能分析器 Oracle Solaris Studio 12.3 Information Library (简体中文) |
可通过以固定的间隔记录分析事件来收集分析数据。该间隔可以是使用系统时钟获取的时间间隔,也可以是特定类型硬件事件的数目。间隔时间结束时,会向系统传送一个信号,并在下一个间隔记录数据。
可通过在各种系统函数和库函数上插入包装函数来收集跟踪数据,以便拦截对函数的调用,并记录有关调用的数据。
可通过调用各种系统例程以获取信息来收集全局数据。全局数据包称为样本。
为可执行文件以及检测过的和可执行文件静态链接的任何共享对象记录函数和指令计数数据。记录函数和指令的执行次数。
收集线程分析数据以支持线程分析器。
分析数据和跟踪数据都包含有关特定事件的信息,并且这两种类型的数据都会转换为性能度量。全局数据不会转换为度量,而是用于提供标记器,这些标记器可用于将程序执行划分为很多时间段。通过全局数据,可以了解该时间段内程序执行的总体情况。
每个分析事件或跟踪事件收集的数据包都包含以下信息:
标识数据的数据包头
高精度的时间戳
线程 ID
轻量级进程 (lightweight process, LWP) ID
处理器 (CPU) ID,在操作系统中可用时
调用堆栈的副本。对于 Java 程序,将记录两个调用堆栈:机器调用堆栈和 Java 调用堆栈。
对于 OpenMP 程序,还会收集当前并行区域的标识符和 OpenMP 状态。
有关线程和轻量级进程的更多信息,请参见第 6 章。
除了通用数据外,每个事件特定的数据包还包含特定于数据类型的信息。
数据类型及其使用方法将在下列子节中介绍:
进行基于时钟的分析时,收集的数据取决于操作系统所提供的度量。
在 Oracle Solaris 下的基于时钟的分析中,每个线程的状态都按固定的时间间隔存储。这种时间间隔称为分析间隔。这些信息存储在一个整数数组中:数组的一个元素用于内核维护的十个微记帐状态中的每一个状态。收集的数据通过性能分析器转换为每个状态所用的时间和分析间隔的精度。缺省分析间隔约为 10 毫秒 (10 ms)。收集器提供的高精度分析间隔大约为 1 毫秒,低精度分析间隔大约为 100 毫秒,如果操作系统允许,则可使用任意的间隔。运行 collect -h 命令(不带任何其他参数)可列显运行该命令的系统所允许的范围和精度。
表 2-1 Solaris 计时度量
|
对于多线程实验,将计算所有线程的时间(挂钟时间除外)的总和。所定义的挂钟时间对于多程序多数据 (multiple-program multiple-data, MPMD) 目标没有意义。
计时度量按多种类别说明程序消耗时间的位置,并且可用于改善程序的性能。
高用户 CPU 时间说明程序处理大部分工作的位置。此外还可用于查找重新设计算法后可能受益最多的程序部分。
高系统 CPU 时间说明程序在对系统例程的调用中消耗了大量时间。
高等待 CPU 时间说明准备运行的线程数比可用的 CPU 多,或其他进程正在使用 CPU。
高用户锁定时间说明线程无法获得其请求的锁定。
高文本缺页时间意味着链接程序要求的代码会在内存中进行组织,所以很多调用或分支会导致装入新的页面。
高数据缺页时间表明对数据的访问会导致新的页面被装入。重新组织程序的数据结构或算法可以修复此问题。
在 Linux 操作系统下,可用的度量只有用户 CPU 时间。虽然报告的总 CPU 占用时间是准确的,但分析器不可能像在 Oracle Solaris 中那样准确地确定实际系统 CPU 时间的时间比例。虽然分析器显示的信息好像是轻量级进程 (lightweight process, LWP) 数据,但实际上 Linux 中没有 LWP 的数据;所显示的 LWP ID 实际上是线程 ID。
可以在用 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 中安装软件的更多信息,请参见《Adding and Updating Oracle Solaris 11 Software Packages》(添加并更新 Oracle Solaris 11 软件包)。
在 MPI 实验上收集时钟分析数据时,可以显示两个其他度量:
MPI 工作,这种度量将在进程正在 MPI 运行时内执行工作(如处理请求或消息)时进行累积。
MPI 等待,这种度量将在进程正在 MPI 运行时内等待事件、缓冲区或消息时进行累积。
在 Oracle Solaris 上,以串行或并行方式执行工作时,“MPI 工作”会累积。在以下情况下“MPI 等待”会累积:MPI 运行时正在等待进行同步时、该等待正在使用 CPU 时间或正在休眠时,以及正在以并行方式执行工作,但未在 CPU 上调度线程时。
在 Linux 上,仅当进程在用户模式或系统模式下处于活动状态时,“MPI 工作”和“MPI 等待”才会累积。除非您已指定 MPI 应执行忙等待,否则,Linux 上的“MPI 等待”将没有用处。
注 - 如果要在 Linux 上使用 Oracle Message Passing Toolkit 8.2 或 8.2.1,可能需要解决方法。版本 8.1 或 8.2.1c 不需要解决方法,或者如果要使用 Oracle Solaris Studio 编译器,则任何版本都不需要解决方法。
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 状态库。
如果对 OpenMP 程序执行基于时钟的分析,将提供以下两种附加度量:“OpenMP 工作”和“OpenMP 等待”。
在 Oracle Solaris 上,以串行或并行方式执行工作时,“OpenMP 工作”会累积。在以下情况下“OpenMP 等待”会累积:OpenMP 运行时正在等待进行同步时、该等待正在使用 CPU 时间或正在休眠时,以及正在以并行方式执行工作,但未在 CPU 上安排线程时。
在 Linux 操作系统上,仅当进程在用户模式或系统模式下处于活动状态时,“OpenMP 工作”和“OpenMP 等待”才会累积。除非您已指定 OpenMP 应执行忙等待,否则,Linux 上的“OpenMP 等待”将没有用处。
OpenMP 程序的数据可以在三种视图模式中的任一种模式下显示。在用户模式下,从线程按实际从主线程克隆的方式显示,并使其调用堆栈匹配主线程的调用堆栈。调用堆栈中来自 OpenMP 运行时代码 (libmtsk.so) 的框架将进行压缩。在专家用户模式下,主从线程的显示方式不同,编译器生成的显式函数可见,来自 OpenMP 运行时代码 (libmtsk.so) 的框架将进行压缩。对于计算机模式,将显示实际的本机堆栈。
er_kernel 实用程序可以收集有关 Oracle Solaris 内核的基于时钟的分析数据。
er_kernel 实用程序捕获内核分析数据,并将数据记录为分析器实验,其格式与 collect 实用程序在用户程序上创建的实验格式相同。实验可以由 er_print 实用程序或性能分析器进行处理。内核实验可以显示函数数据、调用方-被调用方数据、指令级数据和时间线,但是不能显示源代码行数据(因为大多数 Oracle Solaris 模块不包含行号表)。
有关更多信息,请参见第 9 章。
硬件计数器可跟踪诸如高速缓存未命中次数、高速缓存停止周期、浮点运算、分支误预测、CPU 周期以及执行指令之类的事件。在硬件计数器溢出分析中,当运行线程的 CPU 的指定硬件计数器发生溢出时,收集器将记录一个分析数据包。计数器将重置并继续进行计数。分析数据包中包括溢出值和计数器类型。
各种处理器芯片系列支持同时存在二到十八个硬件计数器寄存器。收集器可收集一个或多个寄存器上的数据。对于每个寄存器,收集器允许选择寄存器的类型来监视溢出并为寄存器设置溢出值。有些硬件计数器可以使用任意寄存器,而有些计数器仅可以使用特定的寄存器。因此,在一个实验中并非可以选择所有的硬件计数器组合。
硬件计数器溢出分析还可以在内核上通过 er_kernel 实用程序执行。有关更多信息,请参见第 9 章。
硬件计数器溢出分析数据由性能分析器转换为计数度量。对于以循环方式计数的计数器,所报告的度量会转换为次数;而对于不以循环方式计数的计数器,所报告的度量为事件计数。在具有多个 CPU 的机器上,用于转换度量的时钟频率为各个 CPU 时钟频率的调和平均数。因为每种类型的处理器都有其自己的一组硬件计数器,并且硬件计数器的数目庞大,因此,此处未列出硬件计数器的度量。下一小节讲述如何找出可用的硬件计数器。
硬件计数器的一个用途是可诊断进出 CPU 的信息流问题。例如,高速缓存未命中次数计数较高表明,重新组织程序的结构来改进数据或文本的位置或提高高速缓存的重用率可以改善程序性能。
某些硬件计数器与其他计数器相互关联。例如,分支误预测和指令高速缓存未命中次数通常是相关的,因为分支误预测会导致将错误的指令装入到指令高速缓存,而这些指令必须替换为正确的指令。这种替换会导致指令高速缓存未命中,或指令转换后备缓冲器 (instruction translation lookaside buffer, ITLB) 未命中,或甚至缺页。
通常会在导致事件和相应事件计数器溢出的指令之后,向硬件计数器溢出传送一条或多条指令:这称为“失控 (skid)”,它会使计数器溢出分析数据难以解释。如果缺少对精确识别因果指令的硬件支持,可以对候选的因果指令尝试合适的回溯搜索。
收集期间支持和指定这种回溯时,硬件计数器分析数据包还包括适用于硬件计数器事件的候选内存引用指令的 PC(program counter,程序计数器)和 EA(effective address,有效地址)。(在分析期间需要进行后续处理来验证候选事件 PC 和 EA)。关于内存引用事件的这一附加信息为各种面向数据的分析(又称为数据空间分析)提供了方便。仅在运行 Oracle Solaris 操作系统的基于 SPARC 的平台上支持回溯。
在一些 SPARC 芯片上,计数器中断是精确的,无需回溯。此类计数器的事件类型后面用字 precise 表示。
如果在与内存有关的精确计数器上前置一个 + 号,将启用内存空间分析,这将有助于确定是哪些程序行或内存地址导致了与内存有关的程序延迟。有关内存空间分析的更多信息,请参见数据空间分析和内存空间分析。
也可以为时钟分析指定候选事件 PC 和 EA 的回溯和记录,尽管数据可能难以解释。硬件计数器回溯更为可靠。
由于硬件计数器是特定于处理器的,因此可以选用的计数器取决于所使用的处理器。性能工具为许多可能常用的计数器提供了别名。通过在特定系统上的终端窗口中键入不带任何其他参数的 collect -h,您可从收集器获得该系统上可用的硬件计数器列表。如果处理器和系统支持硬件计数器分析,则 collect -h 命令会列显两个包含有关硬件计数器信息的列表。第一个列表包含别名为通用名称的硬件计数器;第二个列表包含原始硬件计数器。如果性能计数器子系统和 collect 命令都不知道特定系统上的计数器名称,这些列表将为空。但是,在大多数情况下,可以用数值指定计数器。
以下示例显示了计数器列表中的条目。有别名的计数器将首先显示在列表中,然后是原始硬件计数器列表。该示例中的每一行输出都按打印格式显示。
Aliased HW counters available for profiling: cycles[/{0|1|2|3}],31599989 (`CPU Cycles', alias for Cycles_user; CPU-cycles) insts[/{0|1|2|3}],31599989 (`Instructions Executed', alias for Instr_all; events) loads[/{0|1|2|3}],9999991 (`Load Instructions', alias for Instr_ld; precise load-store events) stores[/{0|1|2|3}],1000003 (`Store Instructions', alias for Instr_st; precise load-store events) dcm[/{0|1|2|3}],1000003 (`L1 D-cache Misses', alias for DC_miss_nospec; precise load-store events) ... Raw HW counters available for profiling: ... Cycles_user[/{0|1|2|3}],1000003 (CPU-cycles) Instr_all[/{0|1|2|3}],1000003 (events) Instr_ld[/{0|1|2|3}],1000003 (precise load-store events) Instr_st[/{0|1|2|3}],1000003 (precise load-store events) DC_miss_nospec[/{0|1|2|3}],1000003 (precise load-store events)
在有别名的硬件计数器列表中,第一个字段(例如,cycles)提供可以在 collect 命令的 -h counter... 参数中使用的别名。此别名还是在 er_print 命令中使用的标识符。
第二个字段列出计数器的可用寄存器,例如 [/{0|1|2|3}]。
第三个字段(例如 9999991)是计数器的缺省溢出值。对于有别名的计数器,选择的缺省值可提供合理的抽样率。由于实际抽样率变化相当大,因此可能需要指定缺省值以外的值。
第四个字段(在圆括号中)包含类型信息。它提供简短描述(例如 CPU Cycles)、原始硬件计数器名称(例如 Cycles_user)以及计数单位类型(例如 CPU-cycles)。
类型信息字段中的可能条目包括下列项:
precise —当指令导致事件计数器溢出时,计数器发生精确中断。您可以在 collect -h 命令的装入存储事件计数器的名称上前置一个 + 号(例如 +dcm),以便在未专门进行分析编译的普通二进制文件上执行内存空间分析。
load、store 或 load-store,则表明计数器与内存相关。您可以在 collect -h 命令中的计数器名称上前置一个 + 号(例如 +dcrm),以请求搜索引发事件的准确指令和虚拟地址。+ 号还可以启用数据空间分析;有关更多信息,请参见`DataObjects`(数据对象)标签、`DataLayout`(数据布局)标签和`MemoryObjects`(内存对象)标签。
not-program-related,计数器会捕获由其他某个程序启动的事件,例如 CPU 到 CPU 的高速缓存嗅探。使用计数器进行分析时将生成警告,并且分析不记录调用堆栈。
如果类型信息的最后一个单词或仅有的单词是:
CPU-cycles,则计数器可用于提供基于时间的度量。针对此类计数器报告的度量在缺省情况下会转换为独占时间和非独占时间,但是也可以显示为事件计数。
events,则度量是非独占和独占事件计数,且无法转换为时间。
在示例中的有别名的硬件计数器列表中,类型信息包含一个单词的,如第一个计数器的 CPU-cycles 和第二个计数器的 events。类型信息包括两个单词的,如第三个计数器的 load-store events。
原始硬件计数器列表中包含的信息是有别名的硬件计数器列表中信息的子集。原始硬件计数器列表中的每行包括由 cputrack(1) 使用的内部计数器名称、可以在其上使用计数器的寄存器编号、缺省溢出值、类型信息和计数器单位(可以是 CPU-cycles 或 events)。
如果计数器度量与运行的程序无关的事件,则类型信息的第一个单词是 not-program-related。对于这样的计数器,分析不会记录调用堆栈,而是显示人工函数 collector_not_program_related 中所用的时间。线程和 LWP ID 会被记录,但没有任何意义。
原始计数器的缺省溢出值为 1000003。对于大多数原始计数器来说,此值并非理想值,所以您应在指定原始计数器时指定溢出值。
在多线程程序中,不同线程执行的任务同步会导致应用程序的执行延迟,例如,一个线程要访问已被其他线程锁定的数据时就不得不等待。这些事件称为同步延迟事件,并通过跟踪对 Solaris 或 pthread 线程函数的调用来收集这些事件。收集和记录这些事件的过程称为同步等待跟踪。等待锁定花费的时间称为等待时间。
只有等待时间超过阈值(单位为微秒)时,才会记录事件。阈值为 0 表示跟踪所有的同步延迟事件,而不管等待时间为何。缺省阈值通过运行校准测试决定,在该测试中对线程库的调用不会出现任何同步延迟。阈值是这些调用的平均时间与某个因子(当前为 6)的乘积。该过程可防止对此类事件进行记录:即等待时间仅在于调用本身,而与实际的延迟无关。因此,数据量会大大减少,但同步事件的计数可能会被明显低估。
Java 程序不支持同步跟踪。
表 2-2 同步等待跟踪度量
|
通过该信息,您可以确定函数或装入对象对同步例程进行调用时是会经常被阻塞还是会经历很长时间的等待。高同步等待时间表示线程间的争用。您可以通过重新设计算法,尤其是重新组织锁的结构,以便仅包含需要锁定的每个线程的数据来减少争用。
对未正确管理的内存分配和解除分配函数进行调用可能会造成数据的使用效率降低,从而导致应用程序的性能降低。在堆跟踪中,收集器通过插入 C 标准库内存分配函数 malloc、realloc、valloc 和 memalign 以及解除分配函数 free 跟踪内存分配和解除分配请求。对 mmap 的调用被视为内存分配,它允许记录 Java 内存分配的堆跟踪事件。Fortran 函数 allocate 和 deallocate 调用 C 标准库函数,因此会间接跟踪这些例程。
不支持对 Java 程序的堆分析。
表 2-3 内存分配(堆跟踪)度量
|
收集堆跟踪数据有助于识别程序中的内存泄漏,或定位内存分配效率不高的位置。
内存泄漏的另一个常用定义(例如在 dbx 调试工具中)为:内存泄漏是一个动态分配的内存块,在程序的数据空间中没有任何指向它的指针。此处所使用的泄漏定义包括这种替换的定义,但也包括存在指针的内存。
收集器可以收集有关对消息传递接口 (Message Passing Interface, MPI) 库的调用的数据。
使用开源 VampirTrace 5.5.3 发行版来实现 MPI 跟踪。该跟踪可识别以下 VampirTrace 环境变量:
|
有关这些变量的更多信息,请参见 Technische Universität Dresden Web 站点上的 Vampirtrace 用户手册。
在达到缓冲区限制之后发生的 MPI 事件将不会写入跟踪文件,这将导致跟踪不完整。
要去掉限制并获取应用程序的完整跟踪,请将 VT_MAX_FLUSHES 环境变量设置为 0。该设置将导致 MPI API 跟踪收集器在缓冲区已满时刷新磁盘的缓冲区。
要更改缓冲区大小,请设置 VT_BUFFER_SIZE 环境变量。该变量的最佳值取决于要跟踪的应用程序。设置较小的值将增加应用程序可以使用的内存,但是将触发 MPI API 跟踪收集器频繁进行缓冲区刷新。这些缓冲区刷新可能会显著改变应用程序的行为。另一方面,设置较大的值(如 2 G)可以使 MPI API 跟踪收集器刷新缓冲区的次数降至最低,但是将减少应用程序可以使用的内存。如果没有足够的内存可用来容纳缓冲区和应用程序数据,应用程序的某些部分可能会交换至磁盘,从而导致应用程序的行为发生显著改变。
|
表 2-4 MPI 跟踪度量
|
MPI 时间是 MPI 函数中所用的总线程时间。如果还收集了 MPI 状态时间,则除 MPI_Init 和 MPI_Finalize 之外的所有 MPI 函数的 MPI 工作时间加上 MPI 等待时间应大约等于 MPI 工作时间。在 Linux 上,MPI 等待和工作基于用户 CPU 时间加系统 CPU 时间,而 MPI 时间基于实际时间,所以这些数值将不匹配。
当前,仅针对点对点消息收集 MPI 字节和消息计数;不针对集体通信函数记录 MPI 字节和消息计数。“接收的 MPI 字节”度量会计算所有消息中接收的实际字节数。“发送的 MPI 字节”会计算所有消息中发送的实际字节数。“MPI 发送数”会计算发送的消息数,“MPI 接收数”会计算接收的消息数。
收集 MPI 跟踪数据有助于标识 MPI 程序中可能因 MPI 调用而产生性能问题的位置。可能发生的性能问题的例子有负载平衡、同步延迟和通信瓶颈。
全局数据由收集器以称为样本数据包的数据包来记录。每个包中都包含一个数据包头、时间戳、内核的执行统计数据(如缺页和 I/O 数据)、上下文切换以及各种页面驻留(工作集和分页)统计数据。记录在样本包中的数据对程序来说是全局的,且不会转换为性能度量。记录样本包的过程称为抽样。
当程序在 IDE 中进行调试期间或在 dbx 中因任何原因停止时(如在断点处,前提是设置了有关在断点处停止的选项)
使用 dbx collector sample record 命令手动记录样本时
如果代码中包含对 collector_sample 的调用,则在调用该例程时(请参见 数据收集的程序控制)
如果将 -l 选项与 collect 命令一起使用,则在传送指定信号时(请参见 collect(1) 手册页)
开始和终止收集时
使用 dbx collector pause 命令暂停收集时(就在暂停之前)和使用 dbx collector resume 命令恢复收集时(就在恢复之后)
创建后续进程前后
性能工具使用记录在样本包中的数据,按时间周期将数据分组,这称为样本。您可以通过选择一组样本过滤特定于事件的数据,以便只查看这些特定时间周期的信息。您也可以查看每个样本的全局数据。
性能工具不对不同种类的采样点进行区分。要利用采样点进行分析,您应只选择一种点进行记录。具体地说,如果要记录与程序结构或执行序列有关的样本点,则应关闭定期抽样,并使用在 dbx 停止进程时,或将信号传送到正使用 collect 命令记录数据的进程时,或调用收集器 API 函数时记录的样本。