gettimeofday/clock_gettime 女爷i 2022-05-30 02:28 214阅读 0赞 我们写程序的时候经常会使用计时函数,比如RPC中计算超时时间,日志中打印当前时间,性能profile中计算某个函数的执行时间等。在使用时间函数的时候,我们一般默认认为时间函数本身的性能是很高的,对主逻辑的影响可以忽略不计。虽然绝大部分情况下这个假设都没问题,但是了解更多细节能够增加我们对问题的把控力,利于系统的设计和问题的调查。 首先来比较gettimeofday/clock\_gettime的性能。 程序代码见后 Glibc版本: $rpm -qa|grep glibc-comm glibc-common-2.5-81 内核版本: $uname -a 2.6.32-220.23.2 $./a.out -help \[gettimeofday/clock\_gettime\] thread\_number loop\_count $./a.out gettimeofday 1 100000000 gettimeofday(50035480681901) , times : 100000000 thread 1105828160 consume 4000225 us 图1看出,gettimeofday走的是vsyscall\[1\](虚拟系统粗糙的描述就是不经过内核进程的切换就可以调用一段预定好的内核代码),没有线程切换的开销。 图1 gettimeofday 走vsyscall ![39e73b12b356921cf410dc48aa8d4c10.png][] 图2 gettimeofday能将usr态cpu消耗压到100% ![af94e1ea36acff495f3887a29e8197e4.png][] 因为走vsyscall,没有线程切换,所以多线程运行性能跟单线程是类似的。 $./a.out gettimeofday 12 100000000 gettimeofday(51127820371298) , times : 100000000 thread 1201568064 consume 4111854 us $./a.out clock\_gettime 1 100000000 clock\_gettime(50265567600696623) , times : 100000000 thread 1107867968 consume 10242448 us 单线程clock\_gettime大概每次100ns 图3 clock\_gettime 走真正的系统调用 ![6312d40aa7ac1f3e2ea87925777c0bb0.png][] 图4 clock\_gettime 70%的cpu花在sys态,确实进入了系统调用流程 ![e13fa273102311faf401dfd1b5b94afd.png][] 因为开销集中在系统调用本身,而不是花在进程切换上,所以多线程结果跟单线程类似。 $./a.out clock\_gettime 12 100000000 clock\_gettime(50369061997211567) , times : 100000000 thread 1122031936 consume 10226828 us 这里说“开销集中在系统调用本身”意思是说clock\_gettime本身的执行就非常耗费时间,其大概的调用路径是 clock\_gettime -> sys\_call -> sys\_clock\_gettime -> getnstimeofday -> read\_tsc -> native\_read\_tsc <table> <tbody> <tr> <td style="border-color:#c0c0c0;vertical-align:baseline;width:35px;"> <div style="margin-left:0px;"> 1 </div> <div style="margin-left:0px;"> 2 </div> <div style="margin-left:0px;"> 3 </div> <div style="margin-left:0px;"> 4 </div> <div style="margin-left:0px;"> 5 </div> <div style="margin-left:0px;"> 6 </div> <div style="margin-left:0px;"> 7 </div> <div style="margin-left:0px;"> 8 </div> <div style="margin-left:0px;"> 9 </div> <div style="margin-left:0px;"> 10 </div> <div style="margin-left:0px;"> 11 </div> <div style="margin-left:0px;"> 12 </div> <div style="margin-left:0px;"> 13 </div> <div style="margin-left:0px;"> 14 </div> <div style="margin-left:0px;"> 15 </div> <div style="margin-left:0px;"> 16 </div> <div style="margin-left:0px;"> 17 </div> <div style="margin-left:0px;"> 18 </div> <div style="margin-left:0px;"> 19 </div> <div style="margin-left:0px;"> 20 </div> <div style="margin-left:0px;"> 21 </div> <div style="margin-left:0px;"> 22 </div> <div style="margin-left:0px;"> 23 </div> <div style="margin-left:0px;"> 24 </div> <div style="margin-left:0px;"> 25 </div> <div style="margin-left:0px;"> 26 </div> <div style="margin-left:0px;"> 27 </div> <div style="margin-left:0px;"> 28 </div> <div style="margin-left:0px;"> 29 </div> <div style="margin-left:0px;"> 30 </div> <div style="margin-left:0px;"> 31 </div> <div style="margin-left:0px;"> 32 </div> </td> <td style="border-color:#c0c0c0;vertical-align:baseline;"> <div style="margin-left:0px;"> <div style="margin-left:0px;"> <code>void</code> <code>getnstimeofday(</code> <code>struct</code> <code>timespec *ts)</code> </div> <div style="margin-left:0px;"> <code>{ </code> </div> <div style="margin-left:0px;"> <code> </code> <code>unsigned </code> <code>long</code> <code>seq;</code> </div> <div style="margin-left:0px;"> <code> </code> <code>s64 nsecs;</code> </div> <div style="margin-left:0px;"> <code> </code> <code>WARN_ON(timekeeping_suspended);</code> </div> <div style="margin-left:0px;"> <code>do</code> <code>{ </code> </div> <div style="margin-left:0px;"> <code> </code> <code>// 下面代码执行过程中xtime可能会被更改,这里通过持有一个序号来避免显示加锁,如果该代码执行完毕之后,seq并未改变,说明xtime未被更改,此次执行成功,否则重试;无论这里重试与否,CPU都会一直干活;</code> </div> <div style="margin-left:0px;"> <code> </code> <code>seq = read_seqbegin(&xtime_lock);</code> </div> <div style="margin-left:0px;"> <code> </code> <code>*ts = xtime;</code> </div> <div style="margin-left:0px;"> <code> </code> <code>nsecs = timekeeping_get_ns(); </code> <code>//从当前时钟源取更细致的时间精度</code> </div> <div style="margin-left:0px;"> <code> </code> <code>/* If arch requires, add in gettimeoffset() */</code> </div> <div style="margin-left:0px;"> <code> </code> <code>nsecs += arch_gettimeoffset(); </code> <code>// 中断从发出到执行有时间消耗,可能需要做补偿</code> </div> <div style="margin-left:0px;"> <code> </code> <code>} </code> <code>while</code> <code>(read_seqretry(&xtime_lock, seq));</code> </div> <div style="margin-left:0px;"> <code> </code> <code>timespec_add_ns(ts, nsecs); </code> <code>//时间转换</code> </div> <div style="margin-left:0px;"> <code>}</code> </div> <div style="margin-left:0px;"> <code>/* Timekeeper helper functions. */</code> </div> <div style="margin-left:0px;"> <code>static</code> <code>inline</code> <code>s64 timekeeping_get_ns(</code> <code>void</code> <code>)</code> </div> <div style="margin-left:0px;"> <code>{ </code> </div> <div style="margin-left:0px;"> <code> </code> <code>cycle_t cycle_now, cycle_delta;</code> </div> <div style="margin-left:0px;"> <code> </code> <code>struct</code> <code>clocksource *</code> <code>clock</code> <code>;</code> </div> <div style="margin-left:0px;"> <code> </code> <code>/* read clocksource: */</code> </div> <div style="margin-left:0px;"> <code>clock</code> <code>= timekeeper.</code> <code>clock</code> <code>;</code> </div> <div style="margin-left:0px;"> <code>// 使用系统注册的时钟源[2]来读取,当前情况下,一般tsc是默认时钟源</code> </div> <div style="margin-left:0px;"> <code>// /sys/devices/system/clocksource/clocksource0/current_clocksource</code> </div> <div style="margin-left:0px;"> <code>// 下面的调用最终执行native_read_tsc, 里面就是一条汇编指令rdtsc</code> </div> <div style="margin-left:0px;"> <code> </code> <code>cycle_now = </code> <code>clock</code> <code>->read(</code> <code>clock</code> <code>);</code> </div> <div style="margin-left:0px;"> <code> </code> <code>/* calculate the delta since the last update_wall_time: */</code> </div> <div style="margin-left:0px;"> <code> </code> <code>cycle_delta = (cycle_now - </code> <code>clock</code> <code>->cycle_last) & </code> <code>clock</code> <code>->mask;</code> </div> <div style="margin-left:0px;"> <code> </code> <code>/* return delta convert to nanoseconds using ntp adjusted mult. */</code> </div> <div style="margin-left:0px;"> <code> </code> <code>return</code> <code>clocksource_cyc2ns(cycle_delta, timekeeper.mult,</code> </div> <div style="margin-left:0px;"> <code> </code> <code>timekeeper.shift);</code> </div> <div style="margin-left:0px;"> <code>}</code> </div> </div> </td> </tr> </tbody> </table> 上面说到时钟源是可以替换的, $cat /sys/devices/system/clocksource/clocksource0/available\_clocksource tsc hpet acpi\_pm 这是所有可用的时钟源。硬件出问题或者内核bug有时候会使得tsc不可用,于是时钟源默认会切换到hpet,使用hpet后gettimeofday和clock\_gettime性能如何?测试一下。 $sudo bash -c "echo hpet > /sys/devices/system/clocksource/clocksource0/current\_clocksource" $cat /sys/devices/system/clocksource/clocksource0/current\_clocksource hpet $./a.out gettimeofday 1 100000000 gettimeofday(50067118117357) , times : 100000000 thread 1091926336 consume 71748597 us 延时是原来的17倍,大概700ns一次;clock\_gettime 与此类似,因为此时瓶颈已经不是系统调用,而是hpet\_read很慢。 此时大概的调用路径是 clock\_gettime -> sys\_call -> sys\_clock\_gettime -> getnstimeofday -> read\_hpet -> hpet\_readl –> readl 实现非常直白,但是readl是读时钟设备的内存映射,慢是肯定的了。 总结来说,上文制定的内核和glibc版本下,tsc时钟源,gettimeofday 比 clock\_gettime快1倍多,适合做计时用(clock\_gettime使用CLOCK\_REALTIME\_COARSE也是很快的);如果因为tsc不稳定(硬件或者内核bug都可能导致,碰到过),hpet一般不会同时出问题,这时hpet成为了新的时钟源,整体性能下降数十倍,两者没啥区别了。 \[1\]. On vsyscalls and the vDSO : [http://lwn.net/Articles/446528/][http_lwn.net_Articles_446528] \[2\]. Linux内核的时钟中断机制 : [http://wenku.baidu.com/view/4a9f37f24693daef5ef73d32.html][http_wenku.baidu.com_view_4a9f37f24693daef5ef73d32.html] <table> <tbody> <tr> <td style="border-color:#c0c0c0;vertical-align:baseline;width:35px;"> <div style="margin-left:0px;"> 1 </div> <div style="margin-left:0px;"> 2 </div> <div style="margin-left:0px;"> 3 </div> <div style="margin-left:0px;"> 4 </div> <div style="margin-left:0px;"> 5 </div> <div style="margin-left:0px;"> 6 </div> <div style="margin-left:0px;"> 7 </div> <div style="margin-left:0px;"> 8 </div> <div style="margin-left:0px;"> 9 </div> <div style="margin-left:0px;"> 10 </div> <div style="margin-left:0px;"> 11 </div> <div style="margin-left:0px;"> 12 </div> <div style="margin-left:0px;"> 13 </div> <div style="margin-left:0px;"> 14 </div> <div style="margin-left:0px;"> 15 </div> <div style="margin-left:0px;"> 16 </div> <div style="margin-left:0px;"> 17 </div> <div style="margin-left:0px;"> 18 </div> <div style="margin-left:0px;"> 19 </div> <div style="margin-left:0px;"> 20 </div> <div style="margin-left:0px;"> 21 </div> <div style="margin-left:0px;"> 22 </div> <div style="margin-left:0px;"> 23 </div> <div style="margin-left:0px;"> 24 </div> <div style="margin-left:0px;"> 25 </div> <div style="margin-left:0px;"> 26 </div> <div style="margin-left:0px;"> 27 </div> <div style="margin-left:0px;"> 28 </div> <div style="margin-left:0px;"> 29 </div> <div style="margin-left:0px;"> 30 </div> <div style="margin-left:0px;"> 31 </div> <div style="margin-left:0px;"> 32 </div> <div style="margin-left:0px;"> 33 </div> <div style="margin-left:0px;"> 34 </div> <div style="margin-left:0px;"> 35 </div> <div style="margin-left:0px;"> 36 </div> <div style="margin-left:0px;"> 37 </div> <div style="margin-left:0px;"> 38 </div> <div style="margin-left:0px;"> 39 </div> <div style="margin-left:0px;"> 40 </div> <div style="margin-left:0px;"> 41 </div> <div style="margin-left:0px;"> 42 </div> <div style="margin-left:0px;"> 43 </div> <div style="margin-left:0px;"> 44 </div> <div style="margin-left:0px;"> 45 </div> <div style="margin-left:0px;"> 46 </div> <div style="margin-left:0px;"> 47 </div> <div style="margin-left:0px;"> 48 </div> <div style="margin-left:0px;"> 49 </div> <div style="margin-left:0px;"> 50 </div> <div style="margin-left:0px;"> 51 </div> <div style="margin-left:0px;"> 52 </div> <div style="margin-left:0px;"> 53 </div> <div style="margin-left:0px;"> 54 </div> <div style="margin-left:0px;"> 55 </div> <div style="margin-left:0px;"> 56 </div> <div style="margin-left:0px;"> 57 </div> <div style="margin-left:0px;"> 58 </div> <div style="margin-left:0px;"> 59 </div> <div style="margin-left:0px;"> 60 </div> <div style="margin-left:0px;"> 61 </div> <div style="margin-left:0px;"> 62 </div> <div style="margin-left:0px;"> 63 </div> <div style="margin-left:0px;"> 64 </div> <div style="margin-left:0px;"> 65 </div> <div style="margin-left:0px;"> 66 </div> <div style="margin-left:0px;"> 67 </div> <div style="margin-left:0px;"> 68 </div> <div style="margin-left:0px;"> 69 </div> <div style="margin-left:0px;"> 70 </div> <div style="margin-left:0px;"> 71 </div> <div style="margin-left:0px;"> 72 </div> <div style="margin-left:0px;"> 73 </div> </td> <td style="border-color:#c0c0c0;vertical-align:baseline;"> <div style="margin-left:0px;"> <div style="margin-left:0px;"> <code>#include <sys/time.h></code> </div> <div style="margin-left:0px;"> <code>#include <iostream></code> </div> <div style="margin-left:0px;"> <code>#include <time.h></code> </div> <div style="margin-left:0px;"> </div> <div style="margin-left:0px;"> <code>using</code> <code>namespace</code> <code>std;</code> </div> <div style="margin-left:0px;"> </div> <div style="margin-left:0px;"> <code>uint64_t now()</code> </div> <div style="margin-left:0px;"> <code>{ </code> </div> <div style="margin-left:0px;"> <code> </code> <code>struct</code> <code>timeval tv;</code> </div> <div style="margin-left:0px;"> <code> </code> <code>gettimeofday(&tv, NULL);</code> </div> <div style="margin-left:0px;"> <code> </code> <code>return</code> <code>tv.tv_sec * 1000000 + tv.tv_usec;</code> </div> <div style="margin-left:0px;"> <code>}</code> </div> <div style="margin-left:0px;"> </div> <div style="margin-left:0px;"> <code>void</code> <code>* func_gettimeofday(</code> <code>void</code> <code>* p)</code> </div> <div style="margin-left:0px;"> <code>{ </code> </div> <div style="margin-left:0px;"> <code> </code> <code>int32_t c = *(int32_t*)p;</code> </div> <div style="margin-left:0px;"> <code> </code> <code>uint64_t start = now();</code> </div> <div style="margin-left:0px;"> </div> <div style="margin-left:0px;"> <code> </code> <code>uint64_t us = 0;</code> </div> <div style="margin-left:0px;"> <code> </code> <code>int</code> <code>i = 0;</code> </div> <div style="margin-left:0px;"> <code> </code> <code>while</code> <code>(i++ < c) { </code> </div> <div style="margin-left:0px;"> <code> </code> <code>struct</code> <code>timeval tv;</code> </div> <div style="margin-left:0px;"> <code> </code> <code>gettimeofday(&tv, NULL);</code> </div> <div style="margin-left:0px;"> <code> </code> <code>us += tv.tv_usec; </code> <code>// avoid optimize</code> </div> <div style="margin-left:0px;"> <code> </code> <code>}</code> </div> <div style="margin-left:0px;"> <code> </code> <code>cout << </code> <code>"gettimeofday("</code> <code><< us << </code> <code>") , times : "</code> <code><< c << endl;</code> </div> <div style="margin-left:0px;"> <code> </code> <code>cout << </code> <code>"thread "</code> <code><< pthread_self() << </code> <code>" consume "</code> <code><< now() - start << </code> <code>" us"</code> <code><< endl;</code> </div> <div style="margin-left:0px;"> <code> </code> <code>return</code> <code>0;</code> </div> <div style="margin-left:0px;"> <code>}</code> </div> <div style="margin-left:0px;"> </div> <div style="margin-left:0px;"> <code>void</code> <code>* func_clockgettime(</code> <code>void</code> <code>* p)</code> </div> <div style="margin-left:0px;"> <code>{ </code> </div> <div style="margin-left:0px;"> <code> </code> <code>int32_t c = *(int32_t*)p;</code> </div> <div style="margin-left:0px;"> <code> </code> <code>uint64_t start = now();</code> </div> <div style="margin-left:0px;"> </div> <div style="margin-left:0px;"> <code> </code> <code>uint64_t us = 0;</code> </div> <div style="margin-left:0px;"> <code> </code> <code>int</code> <code>i = 0;</code> </div> <div style="margin-left:0px;"> <code> </code> <code>while</code> <code>(i++ < c) { </code> </div> <div style="margin-left:0px;"> <code> </code> <code>struct</code> <code>timespec tp;</code> </div> <div style="margin-left:0px;"> <code> </code> <code>clock_gettime(CLOCK_REALTIME, &tp);</code> </div> <div style="margin-left:0px;"> <code> </code> <code>us += tp.tv_nsec;</code> </div> <div style="margin-left:0px;"> <code> </code> <code>}</code> </div> <div style="margin-left:0px;"> <code> </code> <code>cout << </code> <code>"clock_gettime("</code> <code><< us << </code> <code>") , times : "</code> <code><< c << endl;</code> </div> <div style="margin-left:0px;"> <code> </code> <code>cout << </code> <code>"thread "</code> <code><< pthread_self() << </code> <code>" consume "</code> <code><< now() - start << </code> <code>" us"</code> <code><< endl;</code> </div> <div style="margin-left:0px;"> <code> </code> <code>return</code> <code>0;</code> </div> <div style="margin-left:0px;"> <code>}</code> </div> <div style="margin-left:0px;"> </div> <div style="margin-left:0px;"> <code>int</code> <code>main(</code> <code>int</code> <code>argc, </code> <code>char</code> <code>** argv)</code> </div> <div style="margin-left:0px;"> <code>{ </code> </div> <div style="margin-left:0px;"> <code> </code> <code>if</code> <code>(argc != 4) { </code> </div> <div style="margin-left:0px;"> <code> </code> <code>cout << </code> <code>" [gettimeofday/clock_gettime] thread_number loop_count"</code> <code><< endl;</code> </div> <div style="margin-left:0px;"> <code> </code> <code>exit</code> <code>(-1);</code> </div> <div style="margin-left:0px;"> <code> </code> <code>}</code> </div> <div style="margin-left:0px;"> <code> </code> <code>string mode = string(argv[1]);</code> </div> <div style="margin-left:0px;"> <code> </code> <code>int</code> <code>n = </code> <code>atoi</code> <code>(argv[2]);</code> </div> <div style="margin-left:0px;"> <code> </code> <code>int</code> <code>loop = </code> <code>atoi</code> <code>(argv[3]);</code> </div> <div style="margin-left:0px;"> <code> </code> <code>pthread_t* ts = </code> <code>new</code> <code>pthread_t[n];</code> </div> <div style="margin-left:0px;"> <code> </code> <code>for</code> <code>(</code> <code>int</code> <code>i = 0; i < n; i++) { </code> </div> <div style="margin-left:0px;"> <code> </code> <code>if</code> <code>(mode == </code> <code>"gettimeofday"</code> <code>) { </code> </div> <div style="margin-left:0px;"> <code> </code> <code>pthread_create(ts+i, NULL, func_gettimeofday, &loop);</code> </div> <div style="margin-left:0px;"> <code> </code> <code>} </code> <code>else</code> <code>{ </code> </div> <div style="margin-left:0px;"> <code> </code> <code>pthread_create(ts+i, NULL, func_clockgettime, &loop);</code> </div> <div style="margin-left:0px;"> <code> </code> <code>}</code> </div> <div style="margin-left:0px;"> <code> </code> <code>}</code> </div> <div style="margin-left:0px;"> </div> <div style="margin-left:0px;"> <code> </code> <code>for</code> <code>(</code> <code>int</code> <code>i = 0; i < n; i++) { </code> </div> <div style="margin-left:0px;"> <code> </code> <code>pthread_join(ts[i], NULL);</code> </div> <div style="margin-left:0px;"> <code> </code> <code>}</code> </div> <div style="margin-left:0px;"> </div> <div style="margin-left:0px;"> <code> </code> <code>delete</code> <code>[] ts;</code> </div> <div style="margin-left:0px;"> </div> <div style="margin-left:0px;"> <code> </code> <code>return</code> <code>0;</code> </div> <div style="margin-left:0px;"> <code>}</code> </div> </div> </td> </tr> </tbody> </table> [39e73b12b356921cf410dc48aa8d4c10.png]: /images/20220530/6bbc25e62b224daa8118d80a383cbca1.png [af94e1ea36acff495f3887a29e8197e4.png]: /images/20220530/d53fec4a5b364a8e9fe501af0f2db956.png [6312d40aa7ac1f3e2ea87925777c0bb0.png]: /images/20220530/0e1cf97a8ece4300bde1a9945538c650.png [e13fa273102311faf401dfd1b5b94afd.png]: /images/20220530/1ddf73ef55244e65a28a26f5239a53fe.png [http_lwn.net_Articles_446528]: http://lwn.net/Articles/446528/ [http_wenku.baidu.com_view_4a9f37f24693daef5ef73d32.html]: http://wenku.baidu.com/view/4a9f37f24693daef5ef73d32.html
还没有评论,来说两句吧...