Linux 高性能 获取时间

基础

常用时间函数

The Linux Programming Interface
上看,获取时间最常用的函数是 gettimeofday,常见的中间件也是用该函数么?

1
2
3
#include <sys/time.h>
int gettimeofday(struct timeval *tv, struct timezone *tz);
Returns 0 on success, or1 on error

Redis

扫了下redis的命令,发现redis有个time command. 调用该command能够返回当前服务器的时间戳,从redis源码
)上看,redis也是调用了gettimeofday获取当前的时间

1
2
3
4
5
6
7
8
9
10
void timeCommand(client *c) {
    struct timeval tv;

    

    gettimeofday(&tv,NULL);
    addReplyArrayLen(c,2);
    addReplyBulkLongLong(c,tv.tv_sec);
    addReplyBulkLongLong(c,tv.tv_usec);
}

 

Linux是怎么维护和衡量时间

总的来说,现代操作系统主板上会有个Real Time Clock,记录着当前的时间,通过主板上的电池CMOS Battery维持,假如电池没电了,那我们启动后时间就会出现不正常的情况,通过api去获取时间当然的也会出现相应的问题问题。假如时间出现问题,可以通过手动或者自动的方式校准。

服务器启动的时候就会读取当前时间,保存到kernel,记T1
同时启动TSC,什么是TSC?简单来说就是一个64bit的寄存器,记录着服务器启动以来cpu的cycle,每个cycle的时间就是1/CPU频率,例如cpu 2GHZ, 那么就表示一个cycle的时间是0.5ns
所以获取当前时间的方式就是

1
当前时间 = T1 + TSC * 0.5  //时间的精度为1ns

除了TSC, Linux还有其他方式维护时间,但由于目前主流服务器上都是TSC,所以其他方式的不详述
如何查看当前服务器支持的时间源

1
2
cat /sys/devices/system/clocksource/clocksource0/available_clocksource
tsc hpet acpi_pm

如何查看当前服务器使用的时间源

1
2
cat /sys/devices/system/clocksource/clocksource0/current_clocksource
tsc

Linux最常用的获取时间的接口是gettimeofday, 但除了该方法,Linux还提供了clock_gettime,该api提供了多种获取时间的方式,这里需要注意的是,gettimeofday最终也是调用了clock_gettime就是指定了clockid clock_realtime的时间, 在新版本, linux提供多了一种clockid,CLOCK_REALTIME_COARSE,从manual上看该clockid提供了了一种新的选择“need very fast, but not fine-grained timestamps”。

需要注意的,对于clockid CLOCK_MONOTONIC,这里获取的是一个单调递增的计数器,不受系统时间修改的影响,这个java也会使用到,先记着:)

clockid description
CLOCK_REALTIME System-wide clock that measures real (i.e., wall-clock) time. Setting this clock requires appropriate privileges. This clock is affected by discontinuous jumps in the system time (e.g., if the system administrator manually changes the clock), and by the incremental adjustments performed by adjtime(3) and NTP.
CLOCK_REALTIME_COARSE (since Linux 2.6.32; Linux-specific) A faster but less precise version of CLOCK_REALTIME. Use when you need very fast, but not fine-grained timestamps.
CLOCK_MONOTONIC Clock that cannot be set and represents monotonic time since some unspecified starting point. This clock is not affected by discontinuous jumps in the system time (e.g., if the system administrator manually changes the clock), but is affected by the incremental adjustments performed by adjtime(3) and NTP.
CLOCK_MONOTONIC_COARSE (since Linux 2.6.32; Linux-specific) A faster but less precise version of CLOCK_MONOTONIC. Use when you need very fast, but not fine-grained timestamps.
CLOCK_MONOTONIC_RAW (since Linux 2.6.28; Linux-specific) Similar to CLOCK_MONOTONIC, but provides access to a raw hardware-based time that is not subject to NTP adjustments or the incremental adjustments performed by adjtime(3).
CLOCK_BOOTTIME (since Linux 2.6.39; Linux-specific) Identical to CLOCK_MONOTONIC, except it also includes any time that the system is suspended. This allows applications to get a suspend-aware monotonic clock without having to deal with the complications of CLOCK_REALTIME, which may have discontinuities if the time is changed using settimeofday(2).
CLOCK_PROCESS_CPUTIME_ID (since Linux 2.6.12) Per-process CPU-time clock (measures CPU time consumed by all threads in the process).
CLOCK_THREAD_CPUTIME_ID (since Linux 2.6.12) Thread-specific CPU-time clock.

接下来,我们就来对比各种clockid的性能。
可以参考StackOverflow的这个回答,可以看出
CLOCK_REALTIME和CLOCK_REALTIME_COARSE的性能区别还是比较高,这样对于需要高性能但相对不精确的时间时还是可以选择CLOCK_REALTIME_COARSE的

time (s) => 3 cycles
ftime (ms) => 54 cycles
gettimeofday (us) => 42 cycles
clock_gettime (ns) => 9 cycles (CLOCK_MONOTONIC_COARSE)
clock_gettime (ns) => 9 cycles (CLOCK_REALTIME_COARSE)
clock_gettime (ns) => 42 cycles (CLOCK_MONOTONIC)
clock_gettime (ns) => 42 cycles (CLOCK_REALTIME)
clock_gettime (ns) => 173 cycles (CLOCK_MONOTONIC_RAW)
clock_gettime (ns) => 179 cycles (CLOCK_BOOTTIME)
clock_gettime (ns) => 349 cycles (CLOCK_THREAD_CPUTIME_ID)
clock_gettime (ns) => 370 cycles (CLOCK_PROCESS_CPUTIME_ID)
rdtsc (cycles) => 24 cycles

好吧,上面只是别人的一些经验,直接在服务器上跑测试,可以看出CLOCK_REALTIME平均执行16.15ns, coarse版本的api执行的时间为0, 说明执行的时间小于 1/3.4 ns,好了,至此已经知道Linux常用获取时间的api的效率了

服务器CPU为 3.4GHz

Method min max avg median stdev
CLOCK_REALTIME 14.00 18.00 16.15 16.00 1.24
CLOCK_REALTIME_COARSE 0.00 0.00 0.00 0.00 0.00
CLOCK_MONOTONIC 14.00 19.00 15.88 16.50 1.23
CLOCK_MONOTONIC_RAW 63.00 67.00 64.87 65.00 1.23
CLOCK_MONOTONIC_COARSE 0.00 0.00 0.00 0.00 0.00

nginx是如何做优化

简单说, nginx是通过时间缓存优化时间获取的效率,定时更新缓存,获取时间时从该缓存获取

待补充

补充信息

gettimeofday虽然是存在一定开销,但linux其实已经针对做了很多优化,其实在目前,连系统调用的开销也已经消除了,通过vsdo技术,待持续学习

引用

  1. how-do-computers-keep-track-of-time
  2. select posix clocks
  3. fast equivalent of gettimeofday
  4. HPET vs TSC
  5. the-slow-currenttimemillis
THE END
分享
二维码
< <上一篇
下一篇>>