Oracle® Developer Studio 12.5:性能分析器

退出打印视图

更新时间: 2016 年 6 月
 
 

收集器收集的数据

收集器使用多种方法收集各种数据:

  • 可通过按固定的间隔记录分析事件来收集分析数据 该间隔可以是使用系统时钟获取的时间间隔,也可以是特定类型硬件事件的数目。间隔时间结束时,会向系统传送一个信号,并在下一个间隔记录数据。

  • 可通过在各种系统函数和库函数上插入包装函数来收集跟踪数据,以便拦截对函数的调用,并记录有关调用的数据。

  • 可通过调用各种系统例程获取全局信息来收集抽样数据

  • 通过检测可执行文件和任何共享对象,为可执行文件以及动态打开或静态链接到可执行文件并经过检测的任何共享对象收集函数和指令计数数据。每个函数或基本块的执行次数会记录下来。

  • 收集线程分析数据以支持线程分析器。

分析数据和跟踪数据都包含有关特定事件的信息,并且这两种类型的数据都会转换为性能度量。抽样数据不会转换为度量,而是用于提供标记,这些标记可用于将程序执行划分为很多时间段。通过抽样数据,可以了解该时间段内程序执行的总体情况。

    每个分析事件或跟踪事件收集的数据包都包含以下信息:

  • 标识数据的数据包头。

  • 高精度的时间戳。

  • 线程 ID。

  • 处理器 (CPU) ID,在操作系统中可用时。

  • 调用堆栈的副本。对于 Java 程序,将记录两个调用堆栈:计算机调用堆栈和 Java 调用堆栈。

  • 对于 OpenMP 程序,还会收集当前并行区域的标识符和 OpenMP 状态。

有关线程和轻量级进程的更多信息,请参见了解性能分析器及其数据

除了通用数据外,每个事件特定的数据包还包含特定于数据类型的信息。

时钟分析数据

进行时钟分析时,收集的数据取决于操作系统所提供的信息。

Oracle Solaris 下的时钟分析

在 Oracle Solaris 下的时钟分析中,每个线程的状态都按固定的时间间隔存储。 该时间间隔称为分析间隔。使用分析间隔的精度将收集的数据转换为每个状态所用的时间。

缺省分析间隔约为 10 毫秒 (10 ms)。可以指定高精度分析间隔(大约为 1 毫秒)和低精度分析间隔(大约为 100 毫秒)。如果操作系统允许,也可以指定定制间隔。不带任何其他参数运行 collect -h 可输出系统所允许的范围和精度。

下表显示当实验包含时钟分析数据时,性能分析器和 er_print 可显示的性能度量。请注意,来自所有线程的度量加在了一起。

表 1  Oracle Solaris 上时钟分析的计时度量
度量
定义
Total thread time(总线程时间)
线程在所有状态花费的时间总和。
Total CPU time(CPU 总时间)
在 CPU 中按用户、内核或陷阱模式运行所用的线程时间。
User CPU time(用户 CPU 时间)
以用户模式在 CPU 中运行所花费的线程时间。
System CPU time(系统 CPU 时间)
以内核模式在 CPU 中运行所花费的线程时间。
Trap CPU time(自陷 CPU 时间)
以陷阱模式在 CPU 中运行所花费的线程时间。
User lock time(用户锁定时间)
等待同步锁定所用的线程时间。
Data page fault time(数据缺页时间)
等待数据页所用的线程时间。
Text page fault time(文本缺页时间)
等待文本页所用的线程时间。
Kernel page fault time(内核缺页时间)
等待内核页所用的线程时间。
Stopped time(停止时间)
由于停止而花费的线程时间。
Wait CPU time(等待 CPU 时间)
等待 CPU 所用的线程时间。
Sleep time(休眠时间)
由于休眠而花费的线程时间

计时度量按多种类别说明程序消耗时间的位置,并且可用于改善程序的性能。

  • 高用户 CPU 时间说明程序处理大部分工作的位置。可使用它来查找重新设计算法后可能受益最多的程序部分。

  • 高系统 CPU 时间说明程序在对系统例程的调用中消耗了大量时间。

  • 高等待 CPU 时间说明准备运行的线程数比可用的 CPU 多,或其他进程正在使用 CPU。

  • 高用户锁定时间说明线程无法获得其请求的锁定。

  • 高文本缺页时间意味着链接程序要求的代码会在内存中进行组织,所以很多调用或分支会导致装入新的页面。

  • 高数据缺页时间表明对数据的访问会导致新的页面被装入。重新组织程序的数据结构或算法可以修复此问题。

Linux 下的时钟分析

在 Linux 平台上,只能将时钟数据显示为 CPU 总时间。Linux CPU 时间是用户 CPU 时间和系统 CPU 时间的总和。

针对 OpenMP 程序的时钟分析

如果在 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) 的框架将被禁止。对于计算机模式,将显示实际的本机堆栈。

针对 Oracle Solaris 内核的时钟分析

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 分析。

有关更多信息,请参见内核分析

针对 MPI 程序的时钟分析

可以在用 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 等待时间没有用处。


注 -  也支持其他版本的 MPI。要查看完整列表,请键入不带任何参数的 collect

注 -  如果要在 Linux 上使用 Oracle Message Passing Toolkit 8.2 或 8.2.1,可能需要解决方法。8.1 或 8.2.1c 版本或使用 Oracle Developer 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 状态库。


硬件计数器分析数据

硬件计数器可跟踪诸如高速缓存未命中次数、高速缓存停止周期、浮点运算、分支误预测、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 命令中使用的标识符。

第二个字段提供了计数器的原始名称。例如 loadsInstr_ld 的简称

第三个字段含有类型信息,但有可能是空的(例如 precise load-store)。

    类型信息字段中的可能条目包括下列项:

  • precise,此时基于内存的计数器中断与已知的精确失控一起发生,而且支持用于内存空间分析。对于此类计数器,性能分析器可以(而且在缺省情况下也会)收集内存空间和数据空间数据。有关详细信息,请参见"MemoryObjects"(内存对象)视图中的说明。

  • loadstoreload-store,表明计数器与内存相关。

  • not-program-related,计数器会捕获由其他某个程序启动的事件,例如 CPU 到 CPU 的高速缓存嗅探。使用计数器进行分析时将生成警告,并且分析不记录调用堆栈。

第四个字段含有正在计数的单位类型(例如 events)。

    该单位可以是以下单位之一:

  • CPU-cycles,则计数器可用于提供基于时间的度量。针对此类计数器报告的度量在缺省情况下会转换为独占时间和非独占时间,但是也可以显示为事件计数。

  • events,则度量是非独占和独占事件计数,且无法转换为时间。

第五个字段列出计数器的可用寄存器。例如,0123

第六个字段简要说明了计数器,例如 Load Instructions

原始硬件计数器列表的格式

原始硬件计数器列表中包含的信息是有别名的硬件计数器列表中信息的子集。 原始硬件计数器列表中的每行包括由 cputrack(1) 使用的内部计数器名称、类型信息、计数器单位(可以是 CPU-cyclesevents)以及在其上使用计数器的寄存器编号。

如果计数器度量与运行的程序无关的事件,则类型信息的第一个单词是 not-program-related。对于这样的计数器,分析不会记录调用堆栈,而是显示人工函数 collector_not_program_related 中所用的时间。会记录线程 ID 和 LWP ID,但没有任何意义。

原始计数器的缺省溢出值为 1000003。对于大多数原始计数器来说,此值并非理想值,所以您应在指定原始计数器时指定溢出值。

同步等待跟踪数据

在多线程程序中,同步不同线程执行的任务会导致程序执行延迟。例如,一个线程要访问被其他线程锁定的数据时就不得不等待。这些事件称为同步延迟事件,并通过跟踪对 Solaris 或 pthread 线程函数的调用来收集这些事件。 收集和记录这些事件的过程称为同步等待跟踪。 等待锁时花费的时间称为同步等待时间。

只有等待时间超过阈值(单位为微秒)时,才会记录事件。 阈值为 0 表示跟踪所有的同步延迟事件,而不管等待时间为何。缺省阈值通过运行校准测试确定,在该测试中对线程库的调用不会出现任何同步延迟。 阈值是这些调用的平均时间与某个因子(当前为 6)的乘积。该过程可防止对此类事件进行记录:即等待时间仅在于调用本身,而与实际的延迟无关。因此,数据量会大大减少,但同步事件的计数可能会被明显低估。

对于 Java 程序,同步跟踪可能会涵盖已分析程序中的 Java 方法调用、本机同步调用或同时涵盖这两种调用。

同步等待跟踪数据被转换为下表中的度量。

表 2  同步等待跟踪度量
度量
定义
同步延迟事件计数。
对等待时间超过指定阈值的同步例程的调用数目。
同步等待时间。
超过指定阈值的等待时间的总和。

通过该信息,您可以确定函数或装入对象对同步例程进行调用时是会经常被阻塞还是会经历很长时间的等待。高同步等待时间表示线程间的争用。您可以通过重新设计算法,尤其是重新组织锁的结构,以便仅包含需要锁定的每个线程的数据来减少争用。

堆跟踪(内存分配)数据

对未正确管理的内存分配和取消分配函数进行调用可能会造成数据的使用效率降低,从而导致应用程序的性能降低。在堆跟踪中,收集器通过插入 C 标准库内存分配函数 mallocreallocvallocmemalign 以及取消分配函数 free 跟踪内存分配和取消分配请求。对 mmap 的调用被视为内存分配,它允许记录 Java 内存分配的堆跟踪事件。Fortran 函数 allocatedeallocate 调用 C 标准库函数,因此会间接跟踪这些例程。

不支持对 Java 程序的堆分析。

堆跟踪数据会被转换为以下度量。

表 3  内存分配(堆跟踪)度量
度量
定义
Allocations(分配)
对内存分配函数的调用数
Bytes allocated(分配的字节数)
每次调用内存分配函数时分配的字节总数
Leaks(泄漏)
调用内存分配函数(未对取消分配函数进行相应的调用)的数量
Bytes leaked(泄漏的字节数)
已分配但未取消分配的字节数

收集堆跟踪数据有助于识别程序中的内存泄漏,或定位内存分配效率不高的位置。

查看应用了过滤器的 "Leaks"(泄漏)视图时,显示的泄漏是在过滤标准下完成的内存分配,这些分配在任何时候都不会取消。泄漏不限于在过滤标准下未取消分配的分配。

内存泄漏在此处的定义是:动态分配的但从未释放的内存块,不管进程地址空间中是否存在指向该内存块的指针。(其他工具可能以不同方式定义泄漏,例如将其定义为:已分配但未释放的内存块,但进程地址空间中不再有指向它的指针。)

I/O 跟踪数据

I/O 数据收集跟踪输入/输出系统调用,包括读取和写入。该收集测量调用的持续时间,跟踪文件和描述符以及传输的数据量。您可以使用 I/O 度量识别具有较高传输字节量和较长总线程时间的文件、文件句柄和调用堆栈。

表 4  I/O 跟踪度量
度量
定义
Read Bytes(读取字节数)
每次调用读取函数时读取的字节总数。
Write Bytes(写入字节数)
每次调用写入函数时写入的字节总数。
Read Count(读取计数)
执行读取调用的次数。
Write Count(写入计数)
执行写入调用的次数。
Other I/O Count(其他 I/O 计数)
执行其他 IO 调用的次数
I/O Error Count(I/O 错误计数)
IO 调用期间发生的错误数
Read Time(读取时间)
读取数据花费的秒数
Write Time(写入时间)
写入数据花费的秒数
Other I/O Time(其他 I/O 时间)
执行其他 IO 调用花费的秒数
I/O Error Time(I/O 错误时间)
花费在 IO 错误上的秒数

抽样数据

全局数据由收集器中称为抽样包的数据包来记录。 每个数据包中都包含一个包头、时间戳、内核的执行统计信息(如缺页和 I/O 数据)、上下文切换以及各种页面驻留(工作集和分页)统计信息。记录在抽样包中的数据对程序来说是全局的,且不会转换为性能度量。记录抽样包的过程称为抽样

    在以下情况下,抽样包会被记录下来:

  • 当程序在 dbx 中调试期间因任何原因停止时(如在断点处,前提是设置了有关在断点处停止的选项)。

  • 如果选择了定期抽样,则在抽样间隔时间结束时。抽样间隔被指定为以秒为单位的整数。 缺省值为 1 秒。

  • 使用 dbx collector sample record 命令手动记录抽样时。

  • 如果代码中包含对 collector_sample 的调用,则在调用该例程时(请参见 使用 libcollector 库从程序控制数据收集)。

  • 传送指定信号时(如果将 -l 选项与 collect 命令一起使用,请参见 collect(1) 手册页)。

  • 开始和终止收集时。

  • 使用 dbx collector pause 命令暂停收集时(就在暂停之前)和使用 dbx collector resume 命令恢复收集时(就在恢复之后)。

  • 创建子孙进程前后。

性能工具使用记录在抽样包中的数据按时间段将数据分组,这些称为抽样。 您可以通过选择一组抽样过滤特定于事件的数据,以便只查看这些特定时间段的信息。您也可以查看每个抽样的全局数据。

性能工具不对不同种类的抽样点进行区分。要利用抽样点进行分析,您应只选择一种类型的点进行记录。具体地说,如果要记录与程序结构或执行序列有关的抽样点,则应关闭定期抽样,并使用在 dbx 停止进程时,或将信号传送到正使用 collect 命令记录数据的进程时,或调用收集器 API 函数时记录的抽样。

MPI 跟踪数据

收集器可以收集有关对消息传递接口 (Message Passing Interface, MPI) 库的调用的数据。

使用开源 VampirTrace 5.5.3 发行版来实现 MPI 跟踪。该跟踪可识别以下 VampirTrace 环境变量:

VT_STACKS
控制是否在数据中记录调用堆栈。缺省设置为 1。将 VT_STACKS 设置为 0 将禁用调用堆栈。
VT_BUFFER_SIZE
控制 MPI API 跟踪收集器的内部缓冲区的大小。缺省值为 64M(64 兆字节)。
VT_MAX_FLUSHES
控制在终止 MPI 跟踪前刷新缓冲区的次数。缺省值是 0,用于设置只要缓冲区满了就允许刷新到磁盘。将 VT_MAX_FLUSHES 设置为正数将为刷新缓冲区的次数设置限制。
VT_VERBOSE
启用各种错误和状态消息显示。缺省值为 1,在此设置下会启用紧急错误和状态消息。如果出现问题,请将此变量设置为 2

有关这些变量的更多信息,请参见 Technische Universität Dresden 网站上的 Vampirtrace 用户手册

在达到缓冲区限制之后发生的 MPI 事件将不会写入跟踪文件,这将导致跟踪不完整。

要去掉限制并获取应用程序的完整跟踪,请将 VT_MAX_FLUSHES 环境变量设置为 0。该设置将导致 MPI API 跟踪收集器在缓冲区已满时刷新磁盘的缓冲区。

要更改缓冲区大小,请设置 VT_BUFFER_SIZE 环境变量。该变量的最佳值取决于要跟踪的应用程序。设置较小的值将增加应用程序可以使用的内存,但是将触发 MPI API 跟踪收集器频繁进行缓冲区刷新。这些缓冲区刷新可能会显著改变应用程序的行为。另一方面,设置较大的值(如 2 G)可以使 MPI API 跟踪收集器刷新缓冲区的次数降至最低,但是将减少应用程序可以使用的内存。如果没有足够的内存可用来容纳缓冲区和应用程序数据,应用程序的某些部分可能会交换至磁盘,从而导致应用程序的行为发生显著改变。

以下列表显示会收集数据的函数。

MPI_Abort
MPI_Accumulate
MPI_Address
MPI_Allgather
MPI_Allgatherv
MPI_Allreduce
MPI_Alltoall
MPI_Alltoallv
MPI_Alltoallw
MPI_Attr_delete
MPI_Attr_get
MPI_Attr_put
MPI_Barrier
MPI_Bcast
MPI_Bsend
MPI_Bsend-init
MPI_Buffer_attach
MPI_Buffer_detach
MPI_Cancel
MPI_Cart_coords
MPI_Cart_create
MPI_Cart_get
MPI_Cart_map
MPI_Cart_rank
MPI_Cart_shift
MPI_Cart_sub
MPI_Cartdim_get
MPI_Comm_compare
MPI_Comm_create
MPI_Comm_dup
MPI_Comm_free
MPI_Comm_group
MPI_Comm_rank
MPI_Comm_remote_group
MPI_Comm_remote_size
MPI_Comm_size
MPI_Comm_split
MPI_Comm_test_inter
MPI_Dims_create
MPI_Errhandler_create
MPI_Errhandler_free
MPI_Errhandler_get
MPI_Errhandler_set
MPI_Error_class
MPI_Error_string
MPI_File_close
MPI_File_delete
MPI_File_get_amode
MPI_File_get_atomicity
MPI_File_get_byte_offset
MPI_File_get_group
MPI_File_get_info
MPI_File_get_position
MPI_File_get_position_shared
MPI_File_get_size
MPI_File_get_type_extent
MPI_File_get_view
MPI_File_iread
MPI_File_iread_at
MPI_File_iread_shared
MPI_File_iwrite
MPI_File_iwrite_at
MPI_File_iwrite_shared
MPI_File_open
MPI_File_preallocate
MPI_File_read
MPI_File_read_all
MPI_File_read_all_begin
MPI_File_read_all_end
MPI_File_read_at
MPI_File_read_at_all
MPI_File_read_at_all_begin
MPI_File_read_at_all_end
MPI_File_read_ordered
MPI_File_read_ordered_begin
MPI_File_read_ordered_end
MPI_File_read_shared
MPI_File_seek
MPI_File_seek_shared
MPI_File_set_atomicity
MPI_File_set_info
MPI_File_set_size
MPI_File_set_view
MPI_File_sync
MPI_File_write
MPI_File_write_all
MPI_File_write_all_begin
MPI_File_write_all_end
MPI_File_write_at
MPI_File_write_at_all
MPI_File_write_at_all_begin
MPI_File_write_at_all_end
MPI_File_write_ordered
MPI_File_write_ordered_begin
MPI_File_write_ordered_end
MPI_File_write_shared
MPI_Finalize
MPI_Gather
MPI_Gatherv
MPI_Get
MPI_Get_count
MPI_Get_elements
MPI_Get_processor_name
MPI_Get_version
MPI_Graph_create
MPI_Graph_get
MPI_Graph_map
MPI_Graph_neighbors
MPI_Graph_neighbors_count
MPI_Graphdims_get
MPI_Group_compare
MPI_Group_difference
MPI_Group_excl
MPI_Group_free
MPI_Group_incl
MPI_Group_intersection
MPI_Group_rank
MPI_Group_size
MPI_Group_translate_ranks
MPI_Group_union
MPI_Ibsend
MPI_Init
MPI_Init_thread
MPI_Intercomm_create
MPI_Intercomm_merge
MPI_Irecv
MPI_Irsend
MPI_Isend
MPI_Issend
MPI_Keyval_create
MPI_Keyval_free
MPI_Op_create
MPI_Op_free
MPI_Pack
MPI_Pack_size
MPI_Probe
MPI_Put
MPI_Recv
MPI_Recv_init
MPI_Reduce
MPI_Reduce_scatter
MPI_Request_free
MPI_Rsend
MPI_rsend_init
MPI_Scan
MPI_Scatter
MPI_Scatterv
MPI_Send
MPI_Send_init
MPI_Sendrecv
MPI_Sendrecv_replace
MPI_Ssend
MPI_Ssend_init
MPI_Start
MPI_Startall
MPI_Test
MPI_Test_cancelled
MPI_Testall
MPI_Testany
MPI_Testsome
MPI_Topo_test
MPI_Type_commit
MPI_Type_contiguous
MPI_Type_extent
MPI_Type_free
MPI_Type_hindexed
MPI_Type_hvector
MPI_Type_indexed
MPI_Type_lb
MPI_Type_size
MPI_Type_struct
MPI_Type_ub
MPI_Type_vector
MPI_Unpack
MPI_Wait
MPI_Waitall
MPI_Waitany
MPI_Waitsome
MPI_Win_complete
MPI_Win_create
MPI_Win_fence
MPI_Win_free
MPI_Win_lock
MPI_Win_post
MPI_Win_start
MPI_Win_test
MPI_Win_unlock

MPI 跟踪数据会被转换为以下度量。

表 5  MPI 跟踪度量
度量
定义
MPI Sends(MPI 发送次数)
已启动 MPI 点对点发送数
MPI Bytes Sent(发送的 MPI 字节数)
"MPI Sends"(MPI 发送次数)中的字节数
MPI Receives(MPI 接收次数)
已完成 MPI 点对点接收数
MPI Bytes Received(接收的 MPI 字节数)
"MPI Receives"(MPI 接收次数)中的字节数
MPI Time(MPI 时间)
对 MPI 函数的所有调用所花费的时间
Other MPI Events(其他 MPI 事件)
对既没有发送也没有接收点对点消息的 MPI 函数的调用数

"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 调用而产生性能问题的位置。可能发生的性能问题的例子有负载平衡、同步延迟和通信瓶颈。