
1.3 常用软件工具和时间测量方法
1.3.1 常用软件工具
1.获取软硬件平台参数的软件工具
在Windows系统上可以使用CPU-Z工具,在Linux平台上可以通过查看/proc/cpuinfo获取硬件平台的主要参数。CPU参数主要包括:型号、微结构、主频、核数、Cache层次和主存容量、扩展指令系统的支持等。软件平台参数主要是操作系统、编译器以及相关主要计算库的版本。表1-4给出了一个典型的软硬件平台参数表。
表1-4 软硬件平台参数表

2.编译器
编译器对于软件系统性能优化有着重要的作用,设置各种不同的编译优化选项可以使系统发挥出潜在的性能优势。常见的编译器主要有:GNU组织的gcc编译器、微软公司的Visual Studio(VS)编译器、Intel公司的Intel C编译器(icc)。它们的平台支持情况如表1-5所示。
表1-5 三种主要编译器的平台支持情况

基本编译优化选项
gcc中最常用的编译优化选项为-O2或者-O3。在这个层次上的编译优化往往采用与CPU体系结构无关的优化。例如:循环展开、函数内嵌等。
VS的编译分为Debug和Release两种模式。在Debug模式下,编译器不进行任何优化。在Release模式下,采用和gcc类似的-O2优化方法。
icc的基本编译优化选项也是-O2或者-O3。
3.性能分析工具
通过性能分析工具可以得到程序实际运行中每个函数的被调用次数、累计执行时间、平均执行时间等信息。通过这些信息可以快速发现程序中的热点。最基本的性能分析工具是Linux上的gprof工具。
gprof的基本使用方法
(1)在编译中加入-pg选项。
(2)正常运行可执行程序,将生成gmon.out文件。
(3)使用gprof工具分析gmon.out文件。
在VS中也具有类似功能。在工具栏的“分析”中选择“性能和诊断”,可使用图形化方法得到程序中各个函数的执行时间比例和CPU利用率等数据。
icc系统中有非常完善的性能分析工具,不仅可以获得特定函数的执行时间和比例,还可以得到x86处理器中的具体性能指标,例如Cache命中率、平均IPC等,为程序的优化提供更为丰富的信息。
4.反汇编工具
在必要时需要查看编译器产生的汇编代码,以分析可能存在的问题。不同的编译器具有不同的反汇编方法。
❑Linux上的反汇编工具是objdump,可以反汇编obj文件或者可执行文件。使用-d参数就可以获得汇编代码。
❑在VS中使用“调试”命令下的“窗口”→“反汇编”命令,可以直接在工具中查看程序的汇编代码。可以在字符模式下运行VS包含的dumpbin工具,功能类似于objdump。
例子1.5 编译优化与反汇编
使用gcc在无优化参数和有-O2优化参数的情况下,分别编译程序示例1.1。
使用objdump命令生成两者的汇编程序,并在其中找到〈main〉函数的位置。
不使用优化编译选项的汇编程序如程序示例1.2所示。其中循环变量i存储于堆栈位置-0x4(%rbp),累加和变量s存储于-0x8(%rbp)。位于程序地址40055c到40056a之间的5条指令构成了循环体:
❑40055c地址处的指令将循环变量值取到%eax寄存器中;
❑40055f地址处的指令将%eax的值累加到变量s中;
❑400562地址处的指令将循环变量i加1;
❑400566地址处的指令将循环变量i与99相比较;
❑40056a地址处的指令将在循环变量i小于或等于99时跳转至循环体起始位置。
使用-O2优化选项的汇编指令如程序示例1.3所示。其中最主要的区别在于程序变量i和s不再存储于堆栈中,而是直接存放在寄存器%eax和%esi中。循环体位于地址400458到400460之间,仅包含四条指令,分别用于:①将%eax寄存器累加到%esi寄存器中;②将%eax寄存器加1;③将%eax寄存器与100相比较;④在比较结果为不相等时跳转到循环入口。


程序示例1.1 测试反汇编的C语言代码

程序示例1.2 编译器未优化的汇编代码

程序示例1.3 编译器优化后的汇编代码
1.3.2 时间测量
测量程序的运行时间是性能优化的基本工作。在Linux和Windows平台上具有不同的时间测量方法。
Linux操作系统提供多种精度的时间测量调用,最常用的测量函数是gettimeofday(),可以获得当前的时间,精度可以达到 μs级别,其函数相关数据结构和原型见程序示例1.4。


程序示例1.4 Linux的gettimeofday()
如果需要测量一段程序的执行时间,则需要在程序执行前后分别调用gettimeofday()函数,再计算两者之间的时间值。程序示例1.5测量了Linux平台上function_to_test()函数的执行时间。

程序示例1.5 Linux的时间测量
Windows上时间测量方法也很多。μs级的测量函数一般使用QueryPerformanceFre-quency()和QueryPerformanceCounter()。这两个函数的定义如程序示例1.6所示。

程序示例1.6 Windows平台的时间测量函数
程序示例1.7测量了Windows平台上function_to_test()函数的执行时间。

程序示例1.7 Windows平台的时间测量