热门关键字:  ubuntu  分区  Fedora  linux系统进程  函数

用 OProfile 彻底了解性能

来源: 作者: 时间:2007-03-30 Tag: 点击:
由于在硬件和软件之间有一些意料之外的交互,分析 Linux 操作系统和应用程序的代码可能是很困难的,但评测( profiling )办法可以识别出系统的性能问题。本文介绍的是 Oprofile,这是一种用于 Linux 的评测工具,将包含在即将发布的稳定内核中。

评测 是表示不同性能特性和特征的数据的形式化总结或分析,它通常以图形和表的形式的出现。评测表提供为特定的处理器事件收集的采样的百分数或数量,比如高速缓存线路故障的数量、传输后备缓存( TLB )故障的数量,等等。

Oprofile 是用于 Linux 的若干种评测和性能监控工具中的一种。它可以工作在不同的体系结构上,包括 IA32, IA64 和 AMD Athlon 系列。它的开销小,将被包含在(Linux)2.6 版的内核中。

Oprofile 可以帮助用户识别诸如循环的展开、高速缓存的使用率低、低效的类型转换和冗余操作、错误预测转移等问题。它收集有关处理器事件的信息,其中包括TLB的故障、停机、存储器访问、位于 DCU(数据高速缓存单元)中的总线路数、一个 DCU 故障的周期数,以及不可高速缓存的和可高速缓存的指令的获取数量。Oprofile是一种细粒度的工具,可以为指令集或者为函数、系统调用或中断处理例程收集采样。Oprofile 通过取样来工作。使用收集到的评测数据,用户可以很容易地找出性能问题。

安装 Oprofile

Oprofile 包含在 Linux 2.5 和更高版本的内核中,也包含在大多数较新的 Linux 版本中,包括 Red Hat 9 。用户也可以使用在本文后面 参考资料部分中的链接来下载 Oprofile 。用户需要在启用 Oprofile 的情况下重新编译内核。下面介绍具体做法:

  1. 启动Oprofile:
     #cd /usr/src/linux
     #make xconfig/menuconfig
    

    在评测菜单中启用 Oprofile ,在 .config 文件中设置 CONFIG_PROFILING=yCONFIG_OPROFILE=y 。 另外,还要在 Processor type and features 菜单中启用 Local APIC 和 IO-APIC 。

  2. 按下面命令格式重新编译:
    #make dep (use for 2.4 kernel versions )
    #make bzImage 
    
    启动新内核:

  3. 为了配置和安装 Oprofile 实用工具,键入以下语句:
    #./configure --with-linux=/usr/src/linux/ --with-qt-dir=/usr/lib/qt/
         --with-kernel-support
    #make
    #make install
    

关于系统要求的信息和更加详细的安装指示,请参阅 参考资料部分中的链接。

Oprofile 工具概述:

  • op_help: 列出可用的事件,并带有简短的描述
  • opcontrol: 控制 Oprofile 的数据收集
  • oprofpp: 检索有用的评测数据
  • op_time: 为系统上的所有映像列出相关的评测值
  • op_to_source: 产生带注解的源文件、汇编文件或源文件和汇编文件的混合
  • op_merge: 合并属于同一个应用程序的采样文件
  • op_import: 将采样数据库文件从外部格式(abi)转换为本地格式

启动评测的三个快速步骤:

  1. 启动 profiler(评测器):
    # opcontrol --setup --ctr0-event=CPU_CLK_UNHALTED
        --ctr0-count=600000 --vmlinux=/usr/src/linux-2.4.20/vmlinux
            For RTC mode users, use --rtc-value=2048
     # opcontrol --start
  2. 现在评测器已经运行,用户可以开始做他们做的事情:

  3. 用下面的选项来转储被评测的数据:
    # opcontrol --stop/--shutdown/--dump
    


    Oprofile 分析:高速缓存利用率问题

高速缓存是最靠近处理器执行单元的存储器,它比主存储器容量小得多,也快得多。它可以在处理器芯片的内部,也可以在处理器芯片的外部。高速缓存中存放的是最频繁使用的指令和数据。由于允许对频繁使用的数据进行快速存取,软件运行要比从主存储器中存取数据快得多。在 Intel IA32 P4 中,数据被存储在每条线路 32 字节的高速缓存线路中。

对于多 CPU 的系统来说,当一个 CPU 修改在 CPU 之间共享的数据的时候,在CPU的高速缓存中的高速缓存线路是无效的。

如果数据或指令没有出现在高速缓存中,或者如果高速缓存线路无效的时候,CPU 通过从主存储器中读数据来更新它的高速缓存。负责做这件事情的处理器事件称为 L2_LINES_IN 。从主存储器读数据需要较多的 CPU 周期。Oprofile 可以帮助用户识别类似于清单 1 所列出的高速缓存问题。

清单 1. 存在高速缓存问题的程序代码
/*
 * Shared data being modified by two threads running on different CPUs.
 */
/* shared structure between two threads which will be optimized later*/
struct shared_data_align {
    unsigned int num_proc1;
    unsigned int num_proc2;
};
/* 
 * Shared structure between two threads remains unchanged (non optimized)
 * This is required in order to collect some samples for the L2_LINES_IN event.
 */
struct shared_data_nonalign {
    unsigned int num_proc1;
    unsigned int num_proc2;
};
/*
 * In the example program below, the parent process creates a clone
 * thread sharing its memory space. The parent thread running on one CPU 
 * increments the num_proc1 element of the common and common_aln. The cloned
 * thread running on another CPU increments the value of num_proc2 element of
 * the common and common_aln structure.
 */
/* Declare global data */
struct shared_data_nonalign common_aln;
/*Declare local shared data */
struct shared_data_align common;
    /* Now clone a thread sharing memory space with the parent process */
    if ((pid = clone(func1, buff+8188, CLONE_VM, &common)) < 0) {
        perror("clone");
        exit(1);
    }
    
    /* Increment the value of num_proc1 in loop */
    for (j = 0; j < 200; j++)
        for(i = 0; i < 100000; i++) {
            common.num_proc1++;
        }
    /* Increment the value of num_proc1 in loop */
    for (j = 0; j < 200; j++)
        for(i = 0; i < 100000; i++) {
            common_aln.num_proc1++;
        }
/*
 * The routine below is called by the cloned thread,
to increment the num_proc2 * element of common and common_aln structure in loop. */ int func1(struct shared_data_align *com) { int i, j; /* Increment the value of num_proc2 in loop */ for (j = 0; j < 200; j++) for (i = 0; i < 100000; i++) { com->num_proc2++; } /* Increment the value of num_proc2 in loop */ for (j = 0; j < 200; j++) for (i = 0; i < 100000; i++) { common_aln.num_proc2++; } }

上面的程序是用来评测事件 L2_LINES_IN 的。请注意在 func1main 中收集的采样:



清单 2. 用于 L2_LINES_IN 的 Oprofile 数据
# opcontrol --setup --ctr0-event=L2_LINES_IN
    --ctr0-count=500 --vmlinux=/usr/src/linux-2.4.20/vmlinux
#opcontrol --start
#./appln
#opcontrol --stop
#oprofpp -l ./appln
Cpu type: PIII
Cpu speed was (MHz estimation) : 699.57
Counter 0 counted L2_LINES_IN events 
(number of allocated lines in L2) with a unit mask of 0x00 (No unit mask) count 500 vma samples % symbol name 080483d0 0 0 _start 080483f4 0 0 call_gmon_start 08048420 0 0 __do_global_dtors_aux 08048480 0 0 fini_dummy 08048490 0 0 frame_dummy 080484c0 0 0 init_dummy 08048630 0 0 __do_global_ctors_aux 08048660 0 0 init_dummy 08048670 0 0 _fini 080484d0 4107 49.2033 main 080485b8 4240 50.7967 func1

现在使用 CPU_CLK_UNHALTED 事件来评测同一个应用程序(可执行文件),这个事件基本上就是收集无停顿地运行的 CPU 周期数的采样。在该例程中收集到的采样数量与处理器在执行指令时所花的时间成正比。收集的采样越多,处理器执行指令所花的时间就越多。请注意在 mainfunc1 中收集的采样数量:



清单 3. 为 CPU_CLK_UNHALTED 收集的 Oprofile 数据
#oprofpp -l ./appln
Cpu type: PIII
Cpu speed was (MHz estimation) : 699.667
Counter 0 counted CPU_CLK_UNHALTED events 
(clocks processor is not halted) with a unit mask of 0x00 (No unit mask) count 10000 vma samples % symbol name 080483d0 0 0 _start 080483f4 0 0 call_gmon_start 08048420 0 0 __do_global_dtors_aux 08048480 0 0 fini_dummy 08048490 0 0 frame_dummy 080484c0 0 0 init_dummy 08048640 0 0 __do_global_ctors_aux 08048670 0 0 init_dummy 08048680 0 0 _fini 080484d0 40317 49.9356 main 080485bc 40421 50.0644 func1

为了改善性能,现在我们把共享数据结构的两个元素分离到不同的高速缓存线路,从而优化共享的数据结构。在 Intel IA32 P4 处理器中,每条 L2 高速缓存线路的大小是 32 个字节。通过填充 shared_data_align 结构中的第一个元素的28个字节,该结构的元素可以被分离到两个不同的高速缓存线路。现在,父线程修改 shared_data_align 的 num_proc1 ,这导致在首次存取时从 1 号 CPU 的高速缓存线路上读入 num_proc1 。将来父线程对 num_proc1 的存取会导致从该高速缓存线路的数据读入。克隆的线程修改 shared_data_align 的 num_proc2 ,这将导致在 2 号 CPU 的另一条高速缓存线路上获得 num_proc2 。 这两个并行运行的线程分别修改位于不同高速缓存线路上元素 num_proc1num_proc2 。通过把该数据结构的两个元素分离到两条不同的高速缓存线路,一条高速缓存线路的修改就不会导致再次从存储器读入另外一条高速缓存线路。这样,被读入的高速缓存线路的数量就减少了。



清单 4. 经过优化的数据结构
    /*
     * The padding is added to separate the two unsigned ints in such a 
     * way that the two elements num_proc1 and num_proc2 are on two
         * different cache lines.
     */
struct shared_data_align {
    unsigned int num_proc1;
    char padding[28];
    unsigned int num_proc2;
};
    /*
     * This structure remains unchanged, so that some cache lines 
      * read in can be seen in profile data.
     */
struct shared_data_nonalign {
    unsigned int num_proc1;
    unsigned int num_proc2;
};    



相关文章:
apache jsp tomcat 虚拟主机 在加上pure-ftp
squid 优化(解释篇)
调整centos文件打开数
REDHAT AS安装10g错误
用SystemImager克隆系统(一)
openssh 5.1版使用chroot sftp帐号技术
HPUX从入门到提高之三
postfix+vm-pop3+openmail 构造邮件服务器
SecureCRT设置
双机备份方案(resin集群+冷备)
开启rsh服务
Solaris9允许root用户登录ssh
Solairs如何上网?
实战PXE启动安装Redhat AS 5 Linux
RHCT Lab1: Network Installation
RHCE Lab1: Kickstart
RHCE Lab1.1: Auto Installation
apache版本号显示的问题
修改tomcat端口号
RS/6000小型机故障的基本定位方法
Linux下的权限管理-ACL
CactiEZv9监控CentOS5.0
Red Hat Enterprise Linux 5.2 简明安装手册
StorNext 简单安装说明
FreeBSD7 Apache2.2 PHP5 PostgreSQL8.3 Ports安
关于nagios监控系统添加主机和服务脚本
C和C++语言学习总结
apache优化
CentOS+Nginx+PHP+Mysql(1)
Apache服务器限制并发连接和下载速度