评估 Linux on POWER 的性能

使用 Linux 工具分析性能

了解如何对侧重于编译语言(如 C 或C++)环境的 Linux on POWER® 性能问题进行评估。本文将介绍 POWER7® CPI 模型,并演示常用 Linux® 工具的用法,以显示可能的 CPU 停滞、流水线冲突和性能问题。在最后一节中,将分析并优化一个 POWER7 算法。

Adhemerval Zanella Netto, 软件工程师, IBM

Adhemerval Zanella 的照片Adhemerval Netto 是 IBM Linux 技术中心的一名经验丰富的软件工程师,致力于 Power 架构上的性能分析和工具链开发。他负责 spec.org 上有关 Linux 的各种 Power 性能报告和贡献开放源码软件 (glibc)。



Ryan S. Arnold, 顾问软件工程师, IBM

Ryan S. Arnold 的照片Ryan Arnold 是 IBM Linux 技术中心的一名 IBM 顾问软件工程师。他在 GNU 工具链集成、Linux 系统库开发方面拥有丰富的经验。Ryan 是 GLIBC 的 Power Architecture 维护人员,而且也是 Libdfp 主库的维护人员。他还是 "Power Architecture 32-bit Application Binary Interface Supplement 1.0" 的合著者。



2012 年 7 月 16 日

简介

应用程序性能评估在现代化计算机上可能会是一个复杂的任务。常用的工具很难处理所有性能变量。每个工作负载因它所施压的计算机子系统不同而各有不同。与对 IO 密集型或内存密集型程序进行调优相比,对 CPU 密集型的程序进行测量和调优有相当大的差异。在本文中,我们将重点描述编译语言环境(C、C++ 和其他语言)中的 CPU 密集型和内存密集型程序。我们将演示如何:

  • 查找程序 hotspots(在执行指令的发生比例较高的程序中的一个区域、函数和方法)
  • 使用处理器上提供的硬件性能计数器,测量程序在 POWER7 上的性能
  • 确定用于在 Linux 上进行性能评估的工具。

POWER7 的 CPI 模型

从 CPI 指标的讨论开始了解应用程序性能分析。每指令周期 (CPI) 指标是完成一个指令所需的处理器周期数。每个指令被分解为多个阶段:一个经典的 RISC 流水线指令会拥有一个提取阶段,然后是指令解码/寄存器读取、执行、一个可选的内存访问,最后是写回阶段。一个 CPU 可以利用指令级并行性提高其 CPI 指标(用较低的 CPI 值测算):每个阶段都将处理不同阶段中的指令。在优化时,可以尝试尽量降低 CPI 值,以最大限度地提高系统的利用率。图 1 演示了流水线处理器中的优化指令流。

图 1. 流水线处理器的优化指令流
图表显示了独立的指令流,从提取、解码、执行到写回

有时一个阶段并不是完全独立于其他阶段存在的,或者说会发出一个有依赖性的指令,强制处理器满足这种需求,然后再继续执行。例如,对于后随算术指令的内存负载,会使得处理器先将数据提取到缓存或内存中,然后发出算术指令。在出现这种情况时,据说处理器流水线会遇到一个停滞,而该停滞会使流水线发生停滞。 图 2 显示了一个停滞的流水线。

图 2. 一个遭遇停滞的流水线处理器
图表显示了一些依赖性操作,在这些操作中,指令被停滞并等待其他操作来完成

图 1图 2 的示例中,假设在一个完全填充的流水线上执行 11 个操作周期期间(每一个周期内完成一个指令),处理器可以执行 8 个指令。然而,当出现三个周期的停滞时,在相同数量的周期中,只能执行五个指令。性能将损失 40% 左右。根据这种算法,有些停滞是不可避免的;但是,仔细的分析就可以提供一些关于如何改写或调整部分代码的提示和建议,从而避免这些停滞。在文章 “Modern Microprocessors - a 90 minute guide” 中,可以找到有关现代 CPU 的流水线和指令级并行性的更完整的启发性解释(参阅 参考资料)。

CPI Breakdown Model (CBM) 将功能处理器的各阶段与性能计数器相关联,以显示哪个 CPU 功能单元发生了停滞。CBM 依赖于 CPU 架构和处理器模型;Power Architecture 和 Intel Architecture 具有完全不同的 CBM。POWER5 CBM 与 POWER7 CBM 虽然类似,但仍有所不同。图 3 显示了 POWER7 CBM 的一部分。(请参阅 此信息的文字版。)

图 3. 部分 POWER 7 CBM
Power7 的 CPI Breakdown Model 的屏幕截图

在 Power Architecture 中,硬件性能计数器是一组专用寄存器,当处理器中发生某个特定事件时,就会更新寄存器的内容。POWER7 处理器有一个内置的 Performance Monitoring Unit (PMU),每个 PMU 有 6 线程级的 Performance Counter Monitors (PCM)。其中四个线程是可编程的,这意味着它可以在同一时间监测四个事件,并有超过 500 个可能的性能事件。POWER7 性能计数器是按组进行定义的,PMU 在同一时间只能监视同一组的事件。 图 3 显示了一个性能计数器的子集,用于定义 POWER7 CBM。在 图 3 中,概要文件后面的计数器用来表示哪个 CPU 功能单元导致处理器停滞,并提供有关如何通过算法调优来消除停滞的可能提示。

图 3 中,白框是在概要文件中监视的特定 POWER7 PCM。根据他们的值计算灰框 [每个都标有星号 (*)](这些指标没有特定硬件计数器)。

注:在 "Comprehensive PMU Event Reference POWER7" 一文中可以找到全面的 PMU 参考(参阅 参考资料)。


Linux 上的工具

您可以如何使用 POWER7 处理器中的 PCM?虽然您可以在 POWER 上使用各种分析方法,像硬件中断、代码仪表(如 gprof)和运营系统钩子 (systemtap);PCM 提供了直接使用处理器功能的一组广泛的计数器。PCM 分析仪利用操作系统中断持续定期地对处理器寄存器值进行采样。虽然采样分析的结果的数值准确度可能会比指令跟踪结果低一些,但它对系统的整体性能影响较小,并允许目标基准近全速地运行。由此产生的数据是不准确的,是一个有误差的近似值。

Linux 上最常用的两个 PCM 分析工具是 OProfileperf(参阅 参考资料)。虽然两者都使用同样的原理,即沿工作负载的回溯(通过 syscall)不断对特殊的硬件寄存器进行采样,但它们分别采用不同的方式进行配置和使用。

OProfile 工具是面向 Linux 系统的全系统分析器,它能够以较低的开销分析所有运行的代码。它包括用于采集样本数据的一个内核驱动程序和守护程序、几个用来将数据转化成信息的后分析工具。调试符号(gcc 的 -g 选项)仅在需要注释源代码时才是必要的。利用最新的 Linux 2.6 内核,OProfile 可以提供 gprof 风格的 call-graph 分析信息。OProfile 的典型开销为 1-8%,具体取决于采样频率和工作负载。

在 POWER 上,OProfile 通过监视性能硬件计数器组和性能计数器完成其工作,但不同的组不能一起使用。这意味着,如果从相同的工作负载获得不同的性能计数器,则需要使用不同的 OProfile 事件配置多次运行 OProfile。这还意味着,您不能在同一时间监视整个 POWER7 CBM。可以在上述 “POWER7 PMY Detailed Event Description” 文档中定义可用的组,或运行 清单 1 中的命令定义它:

清单 1. OProfile 组清单
# opcontrol -l

清单 2 中的命令演示了一个简单的 OProfile 配置和调用:

清单 2. OProfile POWER7 CPU 周期配置
# opcontrol -l
# opcontrol -–no-vmlinux
# opcontrol -e PM_CYC_GRP1:500000 -e PM_INST_CMPL_GRP1:500000 -e PM_RUN_CYC_GRP1:500000 
-e PM_RUN_INST_CMPL_GRP1:500000
# opcontrol --start

运行 清单 3 中的工作负载。

清单 3. OProfile 运行命令顺序
# opcontrol --dump 
# opcontrol –-stop
# opcontrol --shutdown

要获得性能计数器报告,可以发出 清单 4 中的命令:

清单 4. OProfile 报告生成
# opreport -l > workload_report

注:在 developerWorks 文章 "Identify performance bottlenecks with OProfile for Linux on POWER"(参阅 参考资料)中可以找到 OProfile 的全面指南(尚未针对 POWER7 进行更新)。

Linux 内核 2.6.29 中引入了 perf 工具,该工具可以分析硬件和软件层面的性能事件。perf 工具拥有面向程序的优势,而不是像 OProfile 那样是面向系统的。它有一些预设的性能计数器列表,如 'cpu-cycles OR cycles'、'branch-misses' 或 'L1-icache-prefetch-misses',它能够复用 PMU 组,允许同一时间从不同的组收集多个性能计数器,但要以采样精度为代价。

有一个缺点是,虽然它支持直接收集硬件性能计数器,但 perf 不承认 POWER7 CBM 表示的计数器名称,需要使用原始的十六进制数字来代替它。表 1OProfile 事件与十六进制数字之间的映射,可以与 perf(使用原始事件的记录选项)配合使用,以便利用 POWER7 的 CBM。

表 1. POWER7 perf 事件原始代码
计数器原始代码
PM_RUN_CYC200f4
PM_CMPLU_STALL 4000a
PM_CMPLU_STALL_FXU20014
PM_CMPLU_STALL_DIV40014
PM_CMPLU_STALL_SCALAR40012
PM_CMPLU_STALL_SCALAR_LONG20018
PM_CMPLU_STALL_VECTOR2001c
PM_CMPLU_STALL_VECTOR_LONG4004a
PM_CMPLU_STALL_LSU20012
PM_CMPLU_STALL_REJECT40016
PM_CMPLU_STALL_ERAT_MISS40018
PM_CMPLU_STALL_DCACHE_MISS20016
PM_CMPLU_STALL_STORE2004a
PM_CMPLU_STALL_THRD1001c
PM_CMPLU_STALL_IFU4004c
PM_CMPLU_STALL_BRU4004e
PM_GCT_NOSLOT_CYC100f8
PM_GCT_NOSLOT_IC_MISS2001a
PM_GCT_NOSLOT_BR_MPRED4001a
PM_GCT_NOSLOT_BR_MPRED_IC_MISS4001c
PM_GRP_CMPL30004
PM_1PLUS_PPC_CMPL100f2

:在 IBM Wiki "Using perf on POWER7 systems"(参阅 参考资料)中,可以找到 perf 的全面指南(尚未针对 POWER7 进行更新)。

您可以获得与 perf 配合使用的原始代码,这些原始代码与 libpfm4 项目(参阅 参考资料)的 OProfile 中定义的 POWER7 事件相对应:它们是在 POWER7 特定的头部 (lib/events/power7_events.h) 中定义的。示例程序 examples/showevtinfo 还显示了事件名称和相应的原始十六进制代码。

为了获得计数器信息,分析是一种常见的做法。分析使开发人员能够识别代码执行和数据访问的热点,找到性能敏感的领域,了解内存访问模式等。在开始分析之前,必须制定一个性能评估策略。该程序可能会由各种模块和/或动态共享对象 (DSO) 组成,它可能频繁地使用内核,也可能更多地依赖于数据模式访问(对 L2 或 L3 缓存访问有很大压力),或者侧重于矢量运算单位。下一节 会重点讨论可能的性能评估策略。

性能评估的策略

初始性能评估是通过检查 CPU 周期利用率计数器找到程序热点。要在 POWER7 上做到这一点,则需要监视 表 2 中列出的事件:

表 2. POWER7 CPU 周期利用率计数器
计数器描述
PM_CYCProcessor Cycles
PM_INST_CMPL已完成的 PowerPC Instructions 数量
PM_RUN_CYC可以通过运行锁存器得到控制的 Processor Cycles。操作系统使用运行锁存器来表示它们何时在做有益的工作。运行锁存器通常在 OS 的空闲循环中被清除。通过运行锁存器进行控制,可以过滤出空闲循环。
PM_RUN_INST_CMPL已完成的运行指令数量

运行配有这些事件的 OProfile 将显示处理器在某个符号中花费的总时间。下面是一个使用 IBM Advance Toolchain 5.0 for POWER(参阅 参考资料)编译的 SPECcpu2006 基准套件的 403.gcc 组件的示例概要文件输出。以下是 opreport -l 命令的输出。

清单 5. 403.gcc 基准的 'opreport -' 输出(计数器 PM_CYC_GRP1 和 PM_INST_CMPL_GRP1)
CPU: ppc64 POWER7, speed 3550 MHz (estimated) 
Counted PM_CYC_GRP1 events ((Group 1 pm_utilization) Processor Cycles) with a unit 
mask of 0x00 (No unit mask) count 500000 
Counted PM_INST_CMPL_GRP1 events ((Group 1 pm_utilization) Number of PowerPC 
Instructions that completed.) with a unit mask of 0x00 (No unit mask) count 500000 

samples  %        samples  %        image name      app name        symbol name 
204528    7.9112  32132     1.3848  gcc_base.none   gcc_base.none   reg_is_remote_cons\
                                                                    tant_p.isra.3.part.4 
125218    4.8434  246710   10.6324  gcc_base.none   gcc_base.none   bitmap_operation 
113190    4.3782  50950     2.1958  libc-2.13.so    libc-2.13.so    memset 
90316     3.4934  22193     0.9564  gcc_base.none   gcc_base.none   compute_transp 
89978     3.4804  11753     0.5065  vmlinux         vmlinux         .pseries_dedicated_\
                                                                    idle_sleep 
88429     3.4204  130166    5.6097  gcc_base.none   gcc_base.none   bitmap_element_\
                                                                    allocate 
67720     2.6194  41479     1.7876  gcc_base.none   gcc_base.none   ggc_set_mark 
56613     2.1898  89418     3.8536  gcc_base.none   gcc_base.none   canon_rtx 
53949     2.0868  6985      0.3010  gcc_base.none   gcc_base.none   delete_null_\
                                                                    pointer_checks 
51587     1.9954  26000     1.1205  gcc_base.none   gcc_base.none   ggc_mark_rtx_\
                                                                    children_1 
48050     1.8586  16086     0.6933  gcc_base.none   gcc_base.none   single_set_2 
47115     1.8224  33772     1.4555  gcc_base.none   gcc_base.none   note_stores
清单 6. 403.gcc 基准的 'opreport -' 输出(计数器 PM_RUN_CYC_GRP1 和 PM_RUN_INST_CMPL_GRP1)
Counted PM_RUN_CYC_GRP1 events ((Group 1 pm_utilization) Processor Cycles gated by the 
run latch.  Operating systems use the run latch to indicate when they are doing useful 
work.  The run 
latch is typically cleared in the OS idle loop.  Gating by the run latch filters out 
the idle loop.) with a unit mask of 0x00 (No unit mask) count 500000 
Counted PM_RUN_INST_CMPL_GRP1 events ((Group 1 pm_utilization) Number of run 
instructions completed.) with a unit mask of 0x00 (No unit mask) count 500000 

samples  %        samples  %        samples  %      app name        symbol name 
204538    8.3658  32078     1.3965  gcc_base.none   gcc_base.none   reg_is_remote_consta\
                                                                    nt_p.isra.3.part.4 
124596    5.0961  252227   10.9809  gcc_base.none   gcc_base.none   bitmap_operation 
112326    4.5943  50890     2.2155  libc-2.13.so    libc-2.13.so    memset 
90312     3.6939  21882     0.9527  gcc_base.none   gcc_base.none   compute_transp 
0              0  0              0  vmlinux         vmlinux         .pseries_dedicated\
                                                                    _idle_sleep 
88894     3.6359  124831    5.4346  gcc_base.none   gcc_base.none   bitmap_element_all\
                                                                    ocate 
67995     2.7811  41331     1.7994  gcc_base.none   gcc_base.none   ggc_set_mark
56460     2.3093  89484     3.8958  gcc_base.none   gcc_base.none   canon_rtx
54076     2.2118  6965      0.3032  gcc_base.none   gcc_base.none   delete_null_pointer\
                                                                    _checks
51228     2.0953  26057     1.1344  gcc_base.none   gcc_base.none   ggc_mark_rtx_childr\
                                                                    en_1 
48057     1.9656  16005     0.6968  gcc_base.none   gcc_base.none   single_set_2 
47160     1.9289  33766     1.4700  gcc_base.none   gcc_base.none   note_stores

每个被监视的事件在输出中都是用一对列来表示的。第一列显示针对指定事件从 PCM 收集的样品数量,第二列显示它所表示的样本总数的百分比。正如在该报告所见,符号 reg_is_remote_constant_p 消耗了最多的处理器周期,并且是代码优化的一个很好的候选。此概要文件仅标识哪个符号消耗了最多的 CPU 周期,没有标识处理器流水线是否被充分利用。您可以通过比较计数器的结果,查看流水线的利用率。

查看计数器 PM_INST_CMPL_GRP1(第二对列);符号 bitmap_operation 显示出了比符号 reg_is_remote_constant_p 更高的百分比。完成每个处理器指令的时候,此性能计数器都会递增,而 PM_CYC_GRP1 仅意味着已被利用的 CPU 周期数量。如果没有进一步的分析,这可能表示符号 reg_is_remote_constant_p 包含比符号 bitmap_operation 更多的 CPU 停滞,因为为符号 reg_is_remote_constant_p 完成的指令数量明显更少一些。此概要文件提供了一个有关后续优化工作应该集中在哪个符号上的初始提示。

在您开始挖掘和破解代码之前,应该先了解工作负载是 CPU 密集型的还是内存密集型的,这样做明智之举。这很重要,因为每种工作负载类型的优化方法都非常不同。例如,最常见的内存访问来自缓存或主内存(与 NUMA 远程节点内存访问相反),性能几乎完全依赖于所使用的算法和数据结构。要调查内存访问模式,可以监视 表 3 中的以下两个性能计数器:

表 3. POWER7 内存利用率计数器
计数器描述
PM_MEM0_RQ_DISP读取为主内存分派的请求
PM_MEM0_WQ_DISP写入为主内存分派的请求

这两个计数器可以指示内存访问模式主要来自内存读取、写入,还是两者兼而有之。使用和以前一样的基准(来自 SPECcpu2006 的 403.gcc),概要文件显示如下:

清单 7. 403.gcc 基准的 'opreport -' 输出(计数器 PM_MEM0_RQ_DISP 和 PM_MEM0_WQ_DISP)
CPU: ppc64 POWER7, speed 3550 MHz (estimated) 
Counted PM_MEM0_RQ_DISP_GRP59 events ((Group 59 pm_nest2)  Nest events (MC0/MC1/PB/GX), 
Pair0 Bit1) with a unit mask of 0x00 (No unit mask) count 1000 
Counted PM_MEM0_WQ_DISP_GRP59 events ((Group 59 pm_nest2)  Nest events (MC0/MC1/PB/GX), 
Pair3 Bit1) with a unit mask of 0x00 (No unit mask) count 1000 
samples  %        samples  %        app name                 symbol name 
225841   25.8000  289       0.4086  gcc_base.none            reg_is_remote_constant_p.\
                                                             isra.3.part.4 
90068    10.2893  2183      3.0862  gcc_base.none            compute_transp 
54038     6.1733  308       0.4354  gcc_base.none            single_set_2 
32660     3.7311  2006      2.8359  gcc_base.none            delete_null_pointer_checks 
26352     3.0104  1498      2.1178  gcc_base.none            note_stores 
21306     2.4340  1950      2.7568  vmlinux                  .pseries_dedicated_idle_sl\
                                                             eep 
18059     2.0631  9186     12.9865  libc-2.13.so             memset 
15867     1.8126  659       0.9316  gcc_base.none            init_alias_analysis

要观察的另一组有趣的性能计数器是缓存(L2 和 L3)上的访问压力。下面的示例使用了 perf 来分析使用 RHEL6.2 Linux system GCC 构建的 SPECcpu2006 483.xalancbmk 组件(参阅 参考资料)。此组件大量使用了内存分配例程,因此预计内存子系统的压力会很大。为了分析压力,可以利用 OProfile 监视 表 4 中的下列计数器:

表 4. POWER7 缓存/内存访问计数器
计数器描述
PM_DATA_FROM_L2处理器的 Data Cache 是因为需求负载从本地 L2 重新加载的
PM_DATA_FROM_L3处理器的 Data Cache 是因为需求负载从本地 L3 重新加载的
PM_DATA_FROM_LMEM处理器的 Data Cache 是从附加在该处理器所在的模块的内存进行重新加载的
PM_DATA_FROM_RMEM处理器的 Data Cache 是因为需求负载从附加在与处理器所处模块不同的另一个模块的内存重新加载的

概要文件输出显示如下:

清单 8. 489.Xalancbmk 基准的 'opreport -' 输出(计数器 PM_DATA_FROM_L2_GRP91 和 PM_DATA_FROM_L3_GRP91)
CPU: ppc64 POWER7, speed 3550 MHz (estimated) 
Counted PM_DATA_FROM_L2_GRP91 events ((Group 91 pm_dsource1) The processor's Data Cache
was reloaded from the local L2 due to a demand load.) with a unit mask of 0x00 (No unit
 mask) count 1000 
Counted PM_DATA_FROM_L3_GRP91 events ((Group 91 pm_dsource1) The processor's Data Cache
 was reloaded from the local L3 due to a demand load.) with a unit mask of 0x00 (No unit
 mask) count 1000 
samples  %        samples  %        image name     app name       symbol name 
767827   25.5750  7581      0.2525  gcc_base.none  gcc_base.none  bitmap_element_allocate
377138   12.5618  8341      0.2778  gcc_base.none  gcc_base.none  bitmap_operation 
93334     3.1088  3160      0.1052  gcc_base.none  gcc_base.none  bitmap_bit_p 
70278     2.3408  5913      0.1969  libc-2.13.so   libc-2.13.so   _int_free 
56851     1.8936  22874     0.7618  oprofile       oprofile       /oprofile 
47570     1.5845  2881      0.0959  gcc_base.none  gcc_base.none  rehash_using_reg 
41441     1.3803  8532      0.2841  libc-2.13.so   libc-2.13.so   _int_malloc
清单 9. 489.Xalancbmk 基准的 'opreport -' 输出(计数器 PM_DATA_FROM_LMEM_GRP91 和 PM_DATA_FROM_RMEM_GRP91)
Counted PM_DATA_FROM_LMEM_GRP91 events ((Group 91 pm_dsource1) The processor's Data Cache
was reloaded from memory attached to the same module this proccessor is located on.) with
 a unit mask of 0x00 (No unit mask) count 1000 
Counted PM_DATA_FROM_RMEM_GRP91 events ((Group 91 pm_dsource1) The processor's Data Cache
 was reloaded from memory attached to a different module than this proccessor is located 
on.) with a unit mask of 0x00 (No unit mask) count 1000
samples  %        samples  %        image name     app name       symbol name 
1605      0.3344  0              0  gcc_base.none  gcc_base.none  bitmap_element_allocate
1778      0.3704  0              0  gcc_base.none  gcc_base.none  bitmap_operation 
1231      0.2564  0              0  gcc_base.none  gcc_base.none  bitmap_bit_p 
205       0.0427  0              0  libc-2.13.so   libc-2.13.so   _int_free 
583       0.1215  327      100.000  oprofile       oprofile       /oprofile 
0              0  0              0  gcc_base.none  gcc_base.none  rehash_using_reg 
225       0.0469  0              0  libc-2.13.so   libc-2.13.so   _int_malloc

解释概要文件输出显示,大多数缓存压力来自 L2 访问,几乎不需求从 L3 重载,因为 L2 访问 (PM_DATA_FROM_L2) 的计数器采样值的总数和相对值都比 L3 需求重载 (PM_DATA_FROM_L3) 要高一些。通过进行更全面的分析(通过监视更多计数器),您只能获得进一步的信息,像 L2 访问是否由于缓存未命中而造成 CPU 停滞。从该示例概要文件可以得出的结论是,与缓存访问相比,主内存访问(PM_DATA_FROM_LMEM 事件)相当低,并且没有远程访问(事件 PM_DATA_FROM_RMEM),这意味着没有远程 NUMA 节点内存访问。热点和内存访问模式的分析可以提供优化工作的方向;在本例中,需要进一步进行分析,确定究竟是什么造成 CPU 停顿,因为对于正确识别 CPU 停顿而言,只是简单识别工作负载热点和内存访问模式是远远不够的。

为了制定更好的性能优化策略,需要使用 perf 工具而不是 OProfile 来做进一步的分析,因为需要同时监视很多 POWER7 CMB 计数器,所以 图 3 中显示了 22 个计数器,以便为性能优化提供更好的策略。许多事件都位于不同的组中,这意味着使用 OProfile 需要多次运行相同的工作负载。若指定的计数器位于多个组中,那么 perf 工具将会复用硬件计数器的监视。虽然这会产生一个准确度稍低的结果,但整体结果往往与预期非常近似,并且具有可以在更短时间内完成分析的优势。

下面的示例使用 perf 分析同一个 SPECcpu2006 483.xalancbmk 组件。要分析这个组件,可以发出 清单 10 的命令:

清单 10. 对生成的 POWER7 CBM 发出的 perf 命令
$ /usr/bin/perf stat -C 0 -e r100f2,r4001a,r100f8,r4001c,r2001a,r200f4,r2004a,r4004a,
r4004e,r4004c,r20016,r40018,r20012,r40016,r40012,r20018,r4000a,r2001c,r1001c,r20014,
r40014,r30004 taskset -c 0 ./Xalan_base.none -v t5.xml xalanc.xsl > power7_cbm.dat

此命令会导致 perf 监视通过 -c 指定的 CPU 上的 -e 参数所定义的原始事件。任务集调用确保该组件将只在 CPU 0 上运行。工作负载 ./Xalan_base.none -v t5.xml xalanc.xsl 可以被另一个应用程序替代为概要文件。完成概要文件的处理之后,perf 命令会输出一个简单的表,显示每个原始事件的总数,以及所花费的总秒数:

清单 11. 489.Xalancbmk 基准的 'perf stat' 输出
 Performance counter stats for 'taskset -c 0 ./Xalan_base.none -v t5.xml xalanc.xsl': 


   366,860,486,404 r100f2                                                       [18.15%] 
     8,090,500,758 r4001a                                                       [13.65%] 
    50,655,176,004 r100f8                                                       [ 9.13%] 
    11,358,043,420 r4001c                                                       [ 9.11%] 
    10,318,533,758 r2001a                                                       [13.68%] 
 1,301,183,175,870 r200f4                                                       [18.22%] 
     2,150,935,303 r2004a                                                       [ 9.10%] 
                 0 r4004a                                                       [13.65%] 
   211,224,577,427 r4004e                                                       [ 4.54%] 
   212,033,138,844 r4004c                                                       [ 4.54%] 
   264,721,636,705 r20016                                                       [ 9.09%] 
    22,176,093,590 r40018                                                       [ 9.11%] 
   510,728,741,936 r20012                                                       [ 9.10%] 
    39,823,575,049 r40016                                                       [ 9.07%] 
     7,219,335,816 r40012                                                       [ 4.54%] 
         1,585,358 r20018                                                       [ 9.08%] 
   882,639,601,431 r4000a                                                       [ 9.08%] 
     1,219,039,175 r2001c                                                       [ 9.08%] 
         3,107,304 r1001c                                                       [13.62%] 
   120,319,547,023 r20014                                                       [ 9.09%] 
    50,684,413,751 r40014                                                       [13.62%] 
   366,940,826,307 r30004                                                       [18.16%] 

     461.057870036 seconds time elapsed

为了分析针对 POWER7 CBM 的 perf 输出,我们提供了一个 Python 脚本(在 下载 中检查 power7_cbm.zip),该脚本由收集到的虚拟和硬件计数器的计数器指标组成。要创建一个报告,可以发出 清单 12 中的命令:

清单 12. POWER7 CBM python 脚本调用
$ power7_cbm.py power7_cbm.dat

类似于 清单 13 的输出将被打印出来:

清单 13. 489.Xalancbmk 基准的 'power7_cbm.py' 输出
CPI Breakdown Model (Complete) 

Metric                         :            Value :    Percent 
PM_CMPLU_STALL_DIV             :    49802421337.0 :        0.0 
PM_CMPLU_STALL_FXU_OTHER       :    67578558649.0 :        5.2 
PM_CMPLU_STALL_SCALAR_LONG     :        2011413.0 :        0.0 
PM_CMPLU_STALL_SCALAR_OTHER    :     7195240404.0 :        0.6 
PM_CMPLU_STALL_VECTOR_LONG     :              0.0 :        0.0 
PM_CMPLU_STALL_VECTOR_OTHER    :     1209603592.0 :        0.1 
PM_CMPLU_STALL_ERAT_MISS       :    22193968056.0 :        1.7 
PM_CMPLU_STALL_REJECT_OTHER    :    18190293594.0 :        1.4 
PM_CMPLU_STALL_DCACHE_MISS     :   261865838255.0 :       20.3 
PM_CMPLU_STALL_STORE           :     2001544985.0 :        0.2 
PM_CMPLU_STALL_LSU_OTHER       :   202313206181.0 :       15.7 
PM_CMPLU_STALL_THRD            :        2025705.0 :        0.0 
PM_CMPLU_STALL_BRU             :   208356542821.0 :       16.2 
PM_CMPLU_STALL_IFU_OTHER       :     2171796336.0 :        0.2 
PM_CMPLU_STALL_OTHER           :    30895294057.0 :        2.4 
PM_GCT_NOSLOT_IC_MISS          :     9805421042.0 :        0.8 
PM_GCT_NOSLOT_BR_MPRED         :     7823508357.0 :        0.6 
PM_GCT_NOSLOT_BR_MPRED_IC_MISS :    11059314150.0 :        0.9 
PM_GCT_EMPTY_OTHER             :    20292049774.0 :        1.6 
PM_1PLUS_PPC_CMPL              :   365158978504.0 :       28.3 
OVERHEAD_EXPANSION             :      590057044.0 :        0.0 
Total                                             :       96.1

这份报告以误差范围内的统计值为基础,所以最终的比例并不完全准确。即使误差范围较大,CPU 总停滞中仍然只有大约 20% 是由于数据缓存未命中 (PM_CMPLU_STALL_DCACHE_MISS) 引起的。最后的指令完成百分比 (PM_1PLUS_PPC_CMPL) 也有 28% 左右。

未来的优化应尝试通过降低 CPU 停滞和/或 GCT (Global Completion Table) 百分比将该数字最大化。根据这份报告,另一个分析途径是识别发生停滞的代码。要做到这一点,可以使用 perf record 命令。该命令将追踪原始计数器的性能,并利用进程回溯来创建一个映射,从而识别哪个符号生成最多的硬件事件。这类似于 OProfile 的工作方式。在本例中,要跟踪 PM_CMPLU_STALL_DCACHE_MISS 事件,可以发出 清单 14 中的命令:

清单 14. PM_CMPLU_STALL_DCACHE_MISS 事件的 perf record
$ /usr/bin/perf record -C 0 -e r20016 taskset -c 0 ./Xalan_base.none -v t5.xml xalanc.xsl

perf 命令将利用结果创建一个数据文件(通常是 "perf.dat")。使用 perf record 命令能够以交互方式读取它,如 清单 15 所示:

清单 15. 489.Xalancbmk 基准的 'perf report' 输出
Events: 192  raw 0x20016
    39.58%  Xalan_base.none  Xalan_base.none  [.] xercesc_2_5::ValueStore::contains 
    11.46%  Xalan_base.none  Xalan_base.none  [.] xalanc_1_8::XStringCachedAllocator
     9.90%  Xalan_base.none  Xalan_base.none  [.] xalanc_1_8::XStringCachedAllocator
     7.29%  Xalan_base.none  Xalan_base.none  [.] xercesc_2_5::ValueStore::isDuplica
     5.21%  Xalan_base.none  libc-2.13.so     [.] _int_malloc 
     5.21%  Xalan_base.none  Xalan_base.none  [.] __gnu_cxx::__normal_iterator<xa
     4.17%  Xalan_base.none  libc-2.13.so     [.] __GI___libc_malloc 
     2.08%  Xalan_base.none  libc-2.13.so     [.] malloc_consolidate.part.4 
     1.56%  Xalan_base.none  Xalan_base.none  [.] xalanc_1_8::ReusableArenaBlock<xa
     1.56%  Xalan_base.none  Xalan_base.none  [.] xalanc_1_8::ReusableArenaBlock<xa
     1.04%  Xalan_base.none  libc-2.13.so     [.] __free
[...]

凭借使用 POWER7 CBM 计数器和 perf report 工具得到的分析,您的优化工作可能集中于优化符号 xercesc_2_5::ValueStore::contains(xercesc_2_5::FieldValueMap const*) 上的内存和缓存访问。

这个示例仅仅是可能的分析的一个子集。POWER7 CBM 显示出,虽然数据缓存停滞是引起 CPU 停滞的主要原因,但加载和存储单元 (PM_CMPLU_STALL_LSU) 与分支单元 (PM_CMPLU_STALL_BRU) 都是停滞的原因。进一步分析可以解决这些计数器。

案例研究

以下案例研究将应用这些性能评估策略来分析一个三角函数数学函数实现。可以根据分析结果来确定优化机会。本案例研究中使用的函数是 ISO C hypot 函数,被定义为直角三角形的斜边长度。该函数被 C99, POSIX.1-2001 定义为:

double hypot(double x, double y); hypot() 函数返回 sqrt(x*x+y*y)。 如果成功,该函数将返回一个直角三角形,两个直角边的长度分别是 x 和 y。如果 x 或 y 是无穷大,则返回正无穷大。如果 x 或 y 为 NaN,而另一个参数不是无穷大,则返回 NaN。如果结果溢出,就会发生一个范围错误,该函数会分别返回 HUGE_VAL、HUGE_VALF 或 HUGE_VALL。如果两个参数都异常,结果也是异常的,并且发现范围错误,则返回正确的结果。

该算法虽然看似简单,能够处理 Infinity 和 NaN 的 Floating-Point (FP) 参数,以及与 FP 有关的溢出/下溢,但它对性能影响增加了一些挑战。GNU C Library(参阅 参考资料)提供一个位于 sysdeps/ieee754/dbl-64/e_hypot.c 源树中的 hypot 实现:

附录 中包含该代码示例的许可信息。

清单 16. 默认 GLIBC hypot 源代码
double __ieee754_hypot(double x, double y) 
{ 
        double a,b,t1,t2,y1,y2,w; 
        int32_t j,k,ha,hb; 

        GET_HIGH_WORD(ha,x); 
        ha &= 0x7fffffff; 
        GET_HIGH_WORD(hb,y); 
        hb &= 0x7fffffff; 
        if(hb > ha) {a=y;b=x;j=ha; ha=hb;hb=j;} else {a=x;b=y;} 
        SET_HIGH_WORD(a,ha);    /* a <- |a| */ 
        SET_HIGH_WORD(b,hb);    /* b <- |b| */ 
        if((ha-hb)>0x3c00000) {return a+b;} /* x/y > 2**60 */ 
        k=0; 
        if(ha > 0x5f300000) {   /* a>2**500 */ 
           if(ha >= 0x7ff00000) {       /* Inf or NaN */ 
               u_int32_t low; 
               w = a+b;                 /* for sNaN */ 
               GET_LOW_WORD(low,a); 
               if(((ha&0xfffff)|low)==0) w = a; 
               GET_LOW_WORD(low,b); 
               if(((hb^0x7ff00000)|low)==0) w = b; 
               return w; 
           } 
           /* scale a and b by 2**-600 */ 
           ha -= 0x25800000; hb -= 0x25800000;  k += 600; 
           SET_HIGH_WORD(a,ha); 
           SET_HIGH_WORD(b,hb); 
        } 
        if(hb < 0x20b00000) {   /* b < 2**-500 */ 
            if(hb <= 0x000fffff) {      /* subnormal b or 0 */ 
                u_int32_t low; 
                GET_LOW_WORD(low,b); 
                if((hb|low)==0) return a; 
                t1=0; 
                SET_HIGH_WORD(t1,0x7fd00000);   /* t1=2^1022 */ 
                b *= t1; 
                a *= t1; 
                k -= 1022; 
            } else {            /* scale a and b by 2^600 */ 
                ha += 0x25800000;       /* a *= 2^600 */ 
                hb += 0x25800000;       /* b *= 2^600 */ 
                k -= 600; 
                SET_HIGH_WORD(a,ha); 
                SET_HIGH_WORD(b,hb); 
            } 
        } 
    /* medium size a and b */ 
        w = a-b; 
        if (w>b) { 
            t1 = 0; 
            SET_HIGH_WORD(t1,ha); 
            t2 = a-t1; 
            w  = __ieee754_sqrt(t1*t1-(b*(-b)-t2*(a+t1))); 
        } else { 
            a  = a+a; 
            y1 = 0; 
            SET_HIGH_WORD(y1,hb); 
            y2 = b - y1; 
            t1 = 0; 
            SET_HIGH_WORD(t1,ha+0x00100000); 
            t2 = a - t1; 
            w  = __ieee754_sqrt(t1*y1-(w*(-w)-(t1*y2+t2*b))); 
        } 
        if(k!=0) { 
            u_int32_t high; 
            t1 = 1.0; 
            GET_HIGH_WORD(high,t1); 
            SET_HIGH_WORD(t1,high+(k<<20)); 
            return t1*w; 
        } else return w; 
}

这个实现相当复杂,主要是因为算法执行了许多 FP 到 INT 的逐位转换。它假定某些 FP 操作(如比较和乘法)在使用浮点数指令时比使用定点指令更昂贵。在某些架构上存在这种情况,但在 Power Architecture 上则不然。

评估该实现的第一步是创建一个可以分析的基准。在这种情况下,因为该实现仅仅是一个带有两个参数和一个简单算法的函数(没有内部函数调用或其他路径),所以可以建立一个简单的基准来评估它(查看 下载 中的 hypot_bench.tar.gz)。基准是性能评估的一部分;优化应该加快算法的运行,或者加快利用总工作负载性能的算法关键部分的运行。合成基准(像这个基准)应该代表该函数的正常使用。由于优化工作往往需要消耗大量资源和时间,所以您需要将重点放在最常见的情况或预期行为上。如果试图优化的代码所代表的程序总体使用率较低,那么这往往是一种资源浪费。

由于这是针对单个函数的性能分析,所以您可以跳过热点分析,将重点放在对 CBM 的分析上。使用 hypot_bench.c 中的基准与 perf,CBM 信息如 清单 17 所示:

清单 17. hypot 基准的 'power7_cbm.py' 输出
CPI Breakdown Model (Complete) 

Metric                         :            Value :    Percent 
PM_CMPLU_STALL_DIV             :        8921688.0 :        8.7 
PM_CMPLU_STALL_FXU_OTHER       :    13953382275.0 :        5.0 
PM_CMPLU_STALL_SCALAR_LONG     :    24380128688.0 :        8.7 
PM_CMPLU_STALL_SCALAR_OTHER    :    33862492798.0 :       12.0 
PM_CMPLU_STALL_VECTOR_LONG     :              0.0 :        0.0 
PM_CMPLU_STALL_VECTOR_OTHER    :      275057010.0 :        0.1 
PM_CMPLU_STALL_ERAT_MISS       :         173439.0 :        0.0 
PM_CMPLU_STALL_REJECT_OTHER    :         902838.0 :        0.0 
PM_CMPLU_STALL_DCACHE_MISS     :       15200163.0 :        0.0 
PM_CMPLU_STALL_STORE           :        1837414.0 :        0.0 
PM_CMPLU_STALL_LSU_OTHER       :    94866270200.0 :       33.7 
PM_CMPLU_STALL_THRD            :         569036.0 :        0.0 
PM_CMPLU_STALL_BRU             :    10470012464.0 :        3.7 
PM_CMPLU_STALL_IFU_OTHER       :      -73357562.0 :        0.0 
PM_CMPLU_STALL_OTHER           :     7140295432.0 :        2.5 
PM_GCT_NOSLOT_IC_MISS          :        3586554.0 :        0.0 
PM_GCT_NOSLOT_BR_MPRED         :     1008950510.0 :        0.4 
PM_GCT_NOSLOT_BR_MPRED_IC_MISS :         795943.0 :        0.0 
PM_GCT_EMPTY_OTHER             :    42488384303.0 :       15.1 
PM_1PLUS_PPC_CMPL              :    53138626513.0 :       18.9 
OVERHEAD_EXPANSION             :       30852715.0 :        0.0 
Total                                             :      108.7

分析表明,大多数 CPU 停滞以及因此产生的性能损失都来自 Load and Store Unit(LSU - 计数器 PM_CMPLU_STALL_LSU_OTHER)。LSU 有多个计数器与之关联,然而,在 CPU 停滞分析的过程中,重点是与性能下降有关的计数器。在 POWER 上显示出性能下降的那些计数器与 Load-Hit-Store (LHS) 危害相关。当 CPU 将数据写入一个地址,然后太快尝试再次加载该数据时,就会出现这一个大型停滞。下一步是检查这个特定的算法中是否发生这种停滞,首先应该检查事件 PM_LSU_REJECT_LHS(原始代码 “rc8ac”),如 清单 18 所示。

清单 18. PM_LSU_REJECT_LHS POWER7 事件的 perf record
$ perf record -C 0 -e rc8ac taskset -c 0 ./hypot_bench_glibc
$ perf report
Events: 14K raw 0xc8ac
    79.19%  hypot_bench_gli  libm-2.12.so       [.] __ieee754_hypot
    10.38%  hypot_bench_gli  libm-2.12.so       [.] __hypot
     6.34%  hypot_bench_gli  libm-2.12.so       [.] __GI___finite

概要文件的输出显示,符号 __ieee754_hypot 生成了最多的 PM_LSU_REJECT_LHS 事件。调查编译器所产生的程序集代码,确定哪个指令生成了该事件。展开符号 __ieee754_hypot 以注释程序集,遍历 perf report 屏幕并选择 __ieee754_hypot 符号,如 清单 19 中的输出所示。

清单 19. PM_LSU_REJECT_LHS POWER7 事件的 perf report
         :        00000080fc38b730 <.__ieee754_hypot>:
    0.00 :          80fc38b730:   7c 08 02 a6     mflr    r0
    0.00 :          80fc38b734:   fb c1 ff f0     std     r30,-16(r1)
    0.00 :          80fc38b738:   fb e1 ff f8     std     r31,-8(r1)
   13.62 :          80fc38b73c:   f8 01 00 10     std     r0,16(r1)
    0.00 :          80fc38b740:   f8 21 ff 71     stdu    r1,-144(r1)
   10.82 :          80fc38b744:   d8 21 00 70     stfd    f1,112(r1)
    0.23 :          80fc38b748:   e9 21 00 70     ld      r9,112(r1)
   17.54 :          80fc38b74c:   d8 41 00 70     stfd    f2,112(r1)
    0.00 :          80fc38b750:   79 29 00 62     rldicl  r9,r9,32,33
    0.00 :          80fc38b754:   e9 61 00 70     ld      r11,112(r1)
    0.00 :          80fc38b758:   e8 01 00 70     ld      r0,112(r1)
    8.46 :          80fc38b75c:   d8 21 00 70     stfd    f1,112(r1)
[...]

在代码的前面部分,已使用宏 GET_HIGH_WORD 将一个 float 转换到 integer,以便实现后面的逐位操作。GLIBC 的 math/math_private.h 使用 清单 20 中的代码来定义宏。

清单 20. GET_HIGH_WORD 宏定义
#define GET_HIGH_WORD(i,d)                                      \
do {                                                            \
  ieee_double_shape_type gh_u;                                  \
  gh_u.value = (d);                                             \
  (i) = gh_u.parts.msw;                                         \
} while (0)

在这个宏中,造成 LHS 停滞的罪魁祸首可能是将 float 的属性读取到了内部 value,然后将它读取到变量 i 的操作。POWER7 处理器没有将浮点寄存器的内容逐位转移到定点寄存器的原生指令。在 POWER 上实现该操作的方式是,使用一个存储操作将浮点寄存器中的 FP 数字存储到内存,然后将相同的内存位置加载到一个定点寄存器(通用)。由于内存访问比寄存器操作慢(即使在访问 L1 数据缓存时也如此),在存储过程中,CPU 会停滞,以便完成后续的加载。

:文档 "POWER ISA 2.06 (POWER7)"(参阅 参考资料)包含更多的信息。

最常见的性能计数器事件触发中断,保留一个与执行指令接近的指令 PC 地址。这可能会导致不完全准确的程序集注释。为了缓解这种行为 POWER4,并在以后有一组有限的名称为 marked 的性能计数器。标记的指令在每个时间框架内将产生较少的事件;然而,PC 指令会是准确的,导致准确的程序集注释。标记的事件在 opcontrol -l 获得的 OProfile 计数器列表中带有 PM_MRK 前缀。

仔细检查分析,查看 PM_MRK_LSU_REJECT_LHS 计数器。PM_MRK_LSU_REJECT_LHSPM_LSU_REJECT_LHS 这两个计数器监视相同的性能事件。不过,标记的计数器 (PM_MRK_LSU_REJECT_LHS) 在每个时间框架内会产生较少的事件,但带有更准确的程序集注释。(参见 清单 21。)

清单 21. PM_MRK_LSU_REJECT_LHS POWER7 事件的 perf record
$ perf record -C 0 -e rd082 taskset -c 0 ./hypot_bench_glibc
$ perf report
Events: 256K raw 0xd082
    64.61%  hypot_bench_gli  libm-2.12.so       [.] __ieee754_hypot
    35.33%  hypot_bench_gli  libm-2.12.so       [.] __GI___finite

这会生成 清单 22 中的程序集注释。

清单 22. PM_MRK_LSU_REJECT_LHS POWER7 事件的 perf report
         :        00000080fc38b730 <.__ieee754_hypot>:
[...]
    1.23 :          80fc38b7a8:   c9 a1 00 70     lfd     f13,112(r1)
    0.00 :          80fc38b7ac:   f8 01 00 70     std     r0,112(r1)
   32.66 :          80fc38b7b0:   c8 01 00 70     lfd     f0,112(r1)
[...]
    0.00 :          80fc38b954:   f8 01 00 70     std     r0,112(r1)
    0.00 :          80fc38b958:   e8 0b 00 00     ld      r0,0(r11)
    0.00 :          80fc38b95c:   79 00 00 0e     rldimi  r0,r8,32,0
   61.72 :          80fc38b960:   c9 61 00 70     lfd     f11,112(r1
[...]

另一个符号显示大约有 35% 的生成事件有类似的行为,如 清单 23 所示。

清单 23. perf report 的更多重点
         :        00000080fc3a2610 <.__finitel>>
    0.00 :          80fc3a2610:   d8 21 ff f0     stfd    f1,-16(r1)
  100.00 :          80fc3a2614:   e8 01 ff f0     ld      r0,-16(r1)

根据这些信息,优化工作可能是,通过删除 FP 到 INT 的转换,消除这些停滞。POWER 处理器具有快速、高效的 Float-Point 执行单元,所以不需要使用 Fixed-Point 指令来执行这些计算。POWER 目前在 GLIBC 中使用的算法 (sysdeps/powerpc/fpu/e_hypot.c) 只使用 FP 操作就可以删除所有 LHS 停滞。结果是获得了更简单的算法,如 清单 24 所示。

清单 24. PowerPC GLIBC hypot 源代码
double
__ieee754_hypot (double x, double y)
{
  x = fabs (x);
  y = fabs (y);

  TEST_INF_NAN (x, y);

  if (y > x)
    {
      double t = x;
      x = y;
      y = t;
    }
  if (y == 0.0 || (x / y) > two60)
    {
      return x + y;
    }
  if (x > two500)
    {
      x *= twoM600;
      y *= twoM600;
      return __ieee754_sqrt (x * x + y * y) / twoM600;
    }
  if (y < twoM500)
    {
      if (y <= pdnum)
        {
          x *= two1022;
          y *= two1022;
          return __ieee754_sqrt (x * x + y * y) / two1022;
        }
      else
        {
          x *= two600;
          y *= two600;
          return __ieee754_sqrt (x * x + y * y) / two600;
        }
    }
  return __ieee754_sqrt (x * x + y * y);
}

TEST_INF_NAN 宏是更进一步的小优化,在开始更进一步的 FP 操作之前,它会测试某个数是 NaN 还是 INFINITY(这是由于针对 NaN 和 INFINITY 的操作可能会增加 FP 异常,而函数规范不允许这一点)。在 POWER7 上,isinfisnan 函数调用被编译器优化为 FP 指令,并且不会产生额外的函数调用,而在较旧的处理器(POWER6 和更旧的版本)上,它将对各函数分别生成一个调用。优化基本上是相同的实现,但是内联的,以避免函数调用。

最后,比较这两种实现,执行下面的简单测试。在使用和不使用新算法的情况下重新编译 GLIBC,比较每个基准运行的总时间。默认 GLIBC 实现结果位于 清单 25 中:

清单 25. 带有默认 GLIBC hypot 的基准
$ /usr/bin/time ./hypot_bench_glibc
INF_CASE       : elapsed time: 14:994339 
NAN_CASE       : elapsed time: 14:707085 
TWO60_CASE     : elapsed time: 12:983906 
TWO500_CASE    : elapsed time: 10:589746 
TWOM500_CASE   : elapsed time: 11:215079 
NORMAL_CASE    : elapsed time: 15:325237 
79.80user 0.01system 1:19.81elapsed 99%CPU (0avgtext+0avgdata 151552maxresident)k 
0inputs+0outputs (0major+48minor)pagefaults 0swaps

优化版本的结果如 清单 26 中所示:

清单 26. Benchmark with optimized GLIBC hypot
$ /usr/bin/time ./hypot_bench_glibc 
INF_CASE       : elapsed time: 4:667043 
NAN_CASE       : elapsed time: 5:100940 
TWO60_CASE     : elapsed time: 6:245313 
TWO500_CASE    : elapsed time: 4:838627 
TWOM500_CASE   : elapsed time: 8:946053 
NORMAL_CASE    : elapsed time: 6:245218 
36.03user 0.00system 0:36.04elapsed 99%CPU (0avgtext+0avgdata 163840maxresident)k 
0inputs+0outputs (0major+50minor)pagefaults 0swaps

这最终将获得超过 100% 的性能提高,使基准时间减少一半。

结束语

含有硬件计数器分析的性能评估是一个功能强大的工具,可以了解工作负载在特定处理器上的行为方式,并给出关于可在哪些地方进行性能优化的提示。最新的 POWER7 处理器有数百个可用的性能计数器,所以我们介绍了一个简单的模型,说明如何将工作负载映射到 CPU 停滞。了解 POWER7 CBM 有点复杂,所以我们还介绍了一些适用于 Linux 的工具,以便简化它。性能评估的策略集中在如何找到热点,如何理解应用程序的内存模式,以及如何使用 POWER7 CBM。最后,我们使用了 GLIBC 内的三角函数上所完成的最新优化来解释用于优化代码的性能分析。

附录

根据 GNU Free Documentation License V1.3 的条款,用户有权复制、分发和/或修改本文档;但无权复制、分发和/或修改 Invariant Sections、Front-Cover Texts 和 Back-Cover Texts。从 http://www.gnu.org/licenses/fdl-1.3.txt 可以找到该许可的副本。


下载

描述名字大小
GLIBC hypot 基准hypot_bench.tar.gz6KB
用来格式化 perf 输出的 Python 脚本power7_cbm.zip2KB

参考资料

学习

获得产品和技术

  • 在项目网站上了解有关 OProfile 的更多信息。
  • 了解有关 perf 工具的更多信息,该工具可在 Linux 内核源中维护代码。
  • libpfm4 项目站点上查找面向 perf 的原始 POWER7 代码。
  • 在项目页面查看 GNU C Library
  • 探索 Advance Toolchain for Linux on POWER,这是一个专门针对尽早使用 IBM 硬件特性而定制的一个免费软件应用程序开发产品集合。
  • 以最适合您的方式 IBM 产品评估试用版软件:下载产品试用版、在线试用产品、在云环境中使用产品或在 IBM SOA 人员沙箱 中花几个小时学习如何高效地实现面向服务的架构。

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Linux, Open source
ArticleID=824621
ArticleTitle=评估 Linux on POWER 的性能
publish-date=07162012