摘 要 本文主要针对Intel C++ 9.0编译器在Visual C++ 6.0环境下对一些常用的功能性操作的优化作用进行了测试验证。主要从对采用编译器前后的程序运行时间进行比较来进行判断。测试范围主要集中于对线程的切换时间,转异常以及异常恢复时间,高优先级线程抢先时间以及信号量响应时间方面。此外,还对一些常用的数学函数的运行时间进行了测试比较。得出的测试数据和结论能对采用此编译器的用户有一定的参考价值和借鉴作用。
关键词 Intel C++,Visual C++,编译器,优化
引言
当今许多计算机应用领域,如天气预报、信号处理以及军事上的弹道跟踪等,它们对程序处理速度的要求都是相当高的。否则就会导致结果出现偏差或者失去其意义。要提高程序的运算速度,一般通过以下几个方面的改进措施来进行:(1)采用新的处理速度更快的硬件设备,如更快的CPU,更大的内存,以及更快的I/O设备等。但这显然会导致成本的大量增加,并非是一种适合于任何单位的实际的改进措施。(2)更加优化的程序设计方法,如在程序中引入多线程、并行等处理方法。这是一种比较有效的方法,当然对程序设计人员以及编程人员的水平有更高的要求。(3)采用一些优化软件,这也是一种简便有效的方法。另外如果和其他两种方法配合使用,对于一些要求大幅度提高处理速度的场合下,也将是非常有帮助的。
采用优化型编译器就属于上面介绍的第三种方法。处理器由于受数据相关、条件转移和资源冲突等原因,指令级并行度受到极大的限制。通过优化编译器对指令序列进行重组,以及采用软件与硬件相结合的方法处理数据相关、条件转移和资源冲突等,可以大大提高处理器的指令级并行度,使在一定时间内可发射尽可能多的指令数。另外,优化编译器还能根据处理器中先行指令窗口的大小,把没有数据相关、控制相关和功能部件冲突,或者冲突和相关比较少的指令调度到同一个先行指令窗口中,使这些指令超越它前面的指令先发射到操作部件中去,从而提高功能部件的利用率,这样就能进一步提高处理器的性能。
一般来讲,实现程序的优化调度需要软件(主要是编译器)和硬件的共同结合才能获得比较好的调度效果。Intel C++ 编译器作为一款专门针对Intel 型处理器进行优化的编译器,它们二者的结合从理论上能实现优化调度,能对程序的执行效能有某些方面和一定程度的提高。测试中处理器应选择Intel系列的产品,实际使用的处理器为Intel 奔腾4处理器(1.8GHZ),内存为256MB;操作系统考虑通用性和广泛性,为Windows XP系统,编程环境为Visual C++ 6.0。
线程额外开销测试
针对越来越多的多线程编程应用,线程间的开销诸如线程间的切换时间、高优先级线程的抢先时间以及线程对信号量的响应时间等指标成为制约程序执行速度的一个重要方面。减少线程的额外开销时间对提高程序的运行速度是非常有意义的。
1、测试指标
这里主要包括四个测试指标,(1)线程的切换时间。(2)高优先级线程的抢先时间。(3)信号量响应时间。(4)线程转异常以及异常恢复时间。
2、测试方法
测试中,启动的线程开始无条件循环运行。以对指标(1)的测试为例,循环中只进行运行次数累加和线程切换的操作,无其他附加操作,直至给出结束事件,终止线程并给出线程的运行次数和时间。测试的计算结果都是在忽略线程本身的开销的情况下得出的,这里线程自身所花费的时间通过测试比较占总花费的时间的比例是非常小的,对计算结果没有太大的影响。另外若在线程中加入对线程本身开销的统计,将会引入其他的操作,同样会有一定的时间开销,仍然会有误差存在。测试中对每一个测试项目皆进行五次重复测试,对得出的五个值取平均。
3、测试流程
这里的测试流程以对指标(1)的测试为例以流程图的形式加以说明,后面三项的测试与此相似。
4、测试步骤
4.1 线程切换时间测试的基本步骤
(1)创建两个相同优先级的线程1和2。
(2)运行线程1得到线程1的当前运行次数后,立即切换到线程2。
(3)运行线程2得到线程2的当前运行次数后,立即切换到线程1。
(4)重复第2步和第3步,直到给出结束事件。
(5)得到2至4步所花费的全部时间和两线程分别的运行次数。
(6)计算线程间的切换时间。
这里设所花费的时间为emplasedtime,分别运行的次数为runcount1和runcount2,则切换时间为emplasedtime/ (runcount1+runcount2-1)。以下几项与此类似。
图1 线程切换时间测试流程
4.2 高优先级线程抢先时间测试的基本步骤
(1)创建两个不同优先级的线程,假定线程1的优先级高于线程2。
(2)得到线程1的当前运行次数后,线程1将优先级降到低于线程2,线程1被线程2抢先。
(3)得到线程2的当前运行次数后,线程2将优先级降到低于线程1,线程2被线程1抢先。
(4)重复第2步和第3步,直到给出结束事件。
(5)得到2至4步所花费的全部时间和两线程分别运行的次数。
(6)计算高优先级线程抢先时间。
4.3 信号量响应时间测试的基本步骤
(1)创建信号量1和信号量2。
(2)建有相同优先级的线程1和2,并分别请求获得信号量1和信号量2。
(3)释放一个信号量1。
(4)线程1获得信号量1,得到当前运行次数后,立即释放信号量2。
(5)线程2获得信号量2,得到当前运行次数后,立即释放信号量1。
(6)重复第4和第5步,直到给出结束事件。
(7)得到4至6步所花费的全部时间和两线程分别运行的次数。
(8)计算线程对信号量的响应时间。
4.4 转异常以及从异常恢复时间的测试步骤
(1)创建运行一个线程。
(2)在线程中给出一个异常。
(3)异常处理中得出当前异常次数。
(4)重复第2和第3步,直到给出结束事件。
(5)得到2至4步所花费的全部时间和异常产生的总次数。
(6)计算转异常以及从异常恢复的时间。
5、测试结果
表1 线程额外开销测试对比
测试项目 | 线程切 换时间 | 高优先 级抢占 时间 | 信号量 响应时间 | 转异常以 及从异常 恢复时间 |
未使用Intel C++编译器(us) | 1.12 | 2.27 | 2.38 | 14.59 |
使用Intel C++ 编译器 (us) | 1.09 | 2.26 | 2.37 | 9.21 |
6、测试结论
从以上四项的测试结果可以看出,除了转异常以及从异常恢复在使用了Intel C++编译器后,执行效率有了较大提高(约能提高1/3)外,其他几项在使用编译器前后几乎没有什么变化。从这里可以看出使用Intel C++编译器编译的代码对处理异常时的处理器能有优化效果,执行效率能有较大的提高;对于线程间的响应和切换却几乎达不到优化的效果。
数学函数运算测试
前面提到的诸多应用领域,大量的数学运算是必需的。许多程序的主体就是大量的数学运算,运算速度对程序的执行速度也就会有决定性的影响。若能提高数学运算的速度,相应的程序的执行效率就将会得到提高。
1、测试项目
测试项目包括常用的三角函数运算和一些开方、次方、求模、对数等常规运算。毕竟大量的复杂的运算也是由以上运算组合的。
2、测试方法
这里对每一项的测试都使用相同的测试方法,即让每种运算都运行固定的大量次数(这里采用百万次),得到运行前后的时间值,求出二者之间的差值,即是运行固定次数的时间。再对同一种运算使用Intel C++编译器进行编译前后的运行时间值进行比较即可。测试中进行运算的函数参数皆为双精度型,对于同一种运算在使用Intel C++编译器前后皆使用相同的参数,避免因带入参数的不同而影响对比判断。测试中对每一个测试项目皆进行十次重复测试,对得出的十个值取平均。
3、测试结果
表2 百万次运算时间对比
测试项目 | sin | cos | tan | sqrt | pow | fmod | Log10 |
未采用Intel C++编译器 (s) | 0.171 | 0.139 | 0.228 | 0.046 | 0.373 | 0.262 | 0.120 |
采用Intel C++编译器 (s) | 0.054 | 0.097 | 0.081 | 0.031 | 0.104 | 0.049 | 0.062 |
4、测试结论
从以上对各项数学函数运算所耗时间的测试中可以看出,Intel C++编译器对数学函数运算的优化效果是比较明显的。针对不同的运算,优化效果也不尽相同。对于同种运算在使用Intel C++编译器前后最差的也能将时间缩短至约原来的2/3,最好的能达到约原来的1/5。由此可以看出,若将其用于涉及有较大运算量的程序中,必将较大地提高程序的处理速度,较好的满足我们在应用中对程序速度的要求。
总结
从以上各项指标的对比测试中,可以得出在减少线程间的转换开销方面Intel C++编译器并不能发挥什么作用,对于线程本身转异常以及从异常恢复方面却能有较大的提高。考虑到异常和中断处理机制的相似性,我们也可推断出其对中断处理效率的提高也应该是有较大作用的。在数学函数运算方面,它能发挥出较大的作用,能较明显地提高数学函数运算的效率。因此我们认为经Intel C++编译器编译过的数学函数运算的代码能更为高效的执行,这对包含较多数学运算的应用程序来讲,Intel C++编译器的使用将会对程序执行速度的提高提供帮助,从而满足相关应用领域对处理速度的要求。
>> 本文固定链接: http://www.vcgood.com/archives/1498