Linux平台多线程程序中使用clock()计算程序执行时间存在问题
今天在Linux下测试对一个程序使用openmp进行部分优化后的执行时间的比较。
根据之前在Windows下的经验,判断一个代码段执行的时间,是使用clock()函数在代码段的开始和结束分别产生一个clock_t的值,然后后者减去前者再除以CLOCKS_PER_SEC,得到一个秒值。
这次测试的时候发现了很奇怪的问题,使用openmp优化过后的执行时间居然比优化前的执行时间多了两倍,这个是令人百思不得其解的问题。
源程序为hw.c,分别使用icc hw.c -o icc_hw 和 icc hw.c -openmp -o omp_hw命令进行编译。执行后通过clock()方式的计算发现前者的执行时间为9.71秒,后者的为20.67秒。
刚开始对这种问题非常费解,以为是调度以及线程间同步的问题,于是使用sar监控CPU的使用情况,发现前者会100%的使用某个CPU,而后者在两个CPU上的使用率都只达到48%左右。
不知为什么忽然对这个clock()的计时产生了怀疑,用了身边一个秒表计算之后,发现后者的实际实行时间并没有达到20秒这么久,而只是13秒左右。
这就奇怪了,为什么13秒的时间会被计算成20秒呢。
在网络上查询了一些资料后,学会使用time命令来统计程序的执行时间,结果如下:
#time ./icc_hw icc && time ./omp_hw omp
time 9.710000
real 0m9.723s
user 0m8.156s
sys 0m1.566stime 20.670000
real 0m13.057s
user 0m12.605s
sys 0m8.077s
以上分别为两个程序的执行情况,其中第一行是程序自身的输出,是使用clock()计算出来的时间,而后三行是time命令的结果。real是是实际的执行时间,即从程序开始到程序执行结束时所消耗的时间,包括CPU的用时。CPU用时被划分为user和sys两块:user值表示程序本身,以及它所调用的库中的子例程使用的时间(是否可以理解为usert态消耗的时间?);sys是由程序直接或间接调用的系统调用执行的时间(是否可以理解为系统态消耗的时间?)。可以看出,在单线程情况下通过clock()得到的时间和实际时间是比较相符的,而多线程情况下,就有差异了:clock()得到的时间约等于是user态下的时间与sys态下的执行时间之和,但由于并行的关系,可能有些时间是重叠的,因此最后实际的执行时间其实更短。
在单处理器上,real值和整个CPU用时之差,也就是real - ( user + sys )是所有延迟程序执行的因素的总和。在SMP上,这个值近似为real * number_of_processors - ( user + sys )。这些因素包括:
- 调入程序文本和数据的IO操作
- 获取程序实际使用内存的IO操作
- 由其它程序消耗的CPU用时
- 由操作系统消耗的CPU用时
- Shell下处理计时相对来说很简单,它是以牺牲计时精确性为代价的。相比之下,在语言中进行处理就显得要复杂得多,但精度更高。
这段具体怎么回事还没怎么看懂,留待以后再做分析。至少现在明白一个事情,Linux下统计多线程程序的执行时间时,不能使用clock()的方法,再没有其他更好的办法前,可以使用shell 命令time,或者GNU的time命令(/usr/bin/time,精度要低一些)。
参考资料:
- Linux 下批量计时中的问题, 车皓阳 中科院软件所
