您的位置:首页 > 运维架构 > Linux

Linux下各种时间

2014-12-21 10:52 225 查看
目录[-]

rdtsc

gettimeofday

time

gmtime_r

localtime_r

asctime_r

ctime_r

mktime

strftime和strptime

localtime_r和gmtime_r性能对比

rdtsc

这是一个汇编指令,可以获取CPU的时钟周期,即滴答tick次数,现在随便一个CPU的频率就是几GHz了,所以算算就知道, 一个CPU时钟周期比纳秒还短,这是非常精确获取时间的方法。当然这获取的只是时钟周期,要转换成时间, 还需要获取运行时的CPU频率,通过
cat
/proc/cpuinfo
获取cpu频率,然后时钟tick数除以频率换算得到时间。对该汇编指令的函数封装如下。
#if defined(__i386__)
static __inline__ unsigned long long rdtsc(void)
{
unsigned long long int x;
__asm__ volatile ("rdtsc" : "=A" (x));
return x;
}
#elif defined(__x86_64__)
static __inline__ unsigned long long rdtsc(void)
{
unsigned hi, lo;
__asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 );
}
#endif


gettimeofday

函数原型如下:
int gettimeofday(struct timeval *tv, struct timezone *tz);


获取时间戳,精度达到微秒,时间存储结构体如下,为16字节大小,分别为两个有符号64位整形。
struct timeval {
time_t      tv_sec;     /* seconds */
suseconds_t tv_usec;    /* microseconds */
};


gettimeofday通常用在统计函数处理时间上。举个例子:
/**
* 时间统计
*/
class Task
{
public:
Task(const char* title)
{
strncpy(t, title, 16);
gettimeofday(&tv,NULL);
}

void set_task(const char* title)
{
struct timeval ttv;
gettimeofday(&ttv, NULL);
fprintf(stdout, "%s: %lu ms\n", t,
(ttv.tv_sec-tv.tv_sec)*1000+(ttv.tv_usec-tv.tv_usec)/1000);
strncpy(t, title, 16);
tv = ttv;
}

~Task()
{
struct timeval ttv;
gettimeofday(&ttv, NULL);
fprintf(stdout, "%s: %lu ms\n", t,
(ttv.tv_sec-tv.tv_sec)*1000+(ttv.tv_usec-tv.tv_usec)/1000);
}
private:
struct timeval tv;
char t[16];
};


time

函数原型为:
time_t time(time_t *t);


获取从UNIX元年开始计数的秒数,故精度为秒。参数t不为NULL,秒数会存储在t所指向的内存,time_t就是int64_t类型,可以直接返回结果,故线程安全,而无需提前分配内存空间。该函数得到的秒数是很多函数的输入,因为很多函数都只是对这些秒数做处理,得到更人性化的输出。

gmtime_r

函数原型为:
struct tm *gmtime_r(const time_t *timep, struct tm *result);


你可能会发现还有不带_r的函数版本,不带
_r
的都是线程不安全的,所以不推荐使用。该函数将time_t转换成
struct
tm
,该结构体的定义如下。该函数得到的是没有时区,或者说是时区偏移为0的时间,所谓的格林威治时间(GMT),这也是为什么函数名字中有gm的原因。

逆操作函数为
time_t
timegm(struct tm *tm);
struct
tm
的定义如下:
struct tm {
int tm_sec;         /* seconds */
int tm_min;         /* minutes */
int tm_hour;        /* hours */
int tm_mday;        /* day of the month */
int tm_mon;         /* month */
int tm_year;        /* year */
int tm_wday;        /* day of the week */
int tm_yday;        /* day in the year */
int tm_isdst;       /* daylight saving time */
};


localtime_r

函数原型为:
struct tm *localtime_r(const time_t *timep, struct tm *result);


这个函数跟gmtime_r差不多,不同点就是会考虑用户机器所在的时区
(/etc/timezone)
。如果在程序性能要求很苛刻的条件下,不推荐使用该函数,因为时区的处理导致它会比gmtime_r慢很多,如果你是设计WEB服务器的,打印日志时会大量用到时间,这个时候有两种处理办法,使用gmtime_r后,打印时间时,同时输出+8,这样告诉了用户当前所在时区,但用户在理解这个时间时,需要把+8小时,算进去,包括Nginx在内的很多开源软件是这么干的。另一种方法是,在使用gmtime_r之前,将time(NULL)的结果加上28800,即8小时,这样就太平了,时间也易读,性能也高。有同事踩过这个坑,在做数据过滤时,每条数据打印一条日志(日志写到缓冲区),马上速度变得奇慢,后来发现就是localtime_r这玩意惹的祸。

其实tm还有两个关于时区的变量,即tm_zone和tm_gmtoff,前一个是字符串,如"GMT"或"CST"之类的时区代号,后一个是与格林威治时间的秒数差值。在linux的manpage里没有描述,估计是使用较少吧。

逆操作函数为
time_t
timelocal(struct tm *tm);
,timelocal等同于mktime,但建议使用后者。

asctime_r

函数原型为:
char *asctime_r(const struct tm *tm, char *buf);


将tm结构体的数据转化成人可读的字符串,结构可能是这样的“Fri Aug 9 06:43:28 2013”,buf大小需26字节以上。该函数也不推荐使用,理由很简单,不符合中国人的习惯。推荐使用strftime进行格式化,以得到自己喜欢的时间格式。我喜欢朴素简单的,像2013-09-06 06:43:28这样的,格式化代码如下。
strftime(time_request, 26, "%Y-%m-%d %H:%M:%S", &res);


ctime_r

函数原型如下:
char *ctime_r(const time_t *timep, char *buf);


ctime_r(t)就等于
asctime(localtime_r(t))
,方便一步到位。

mktime

函数原型为:
time_t mktime(struct tm *tm);


从参数上就能知道,它是localtime的逆过程,等同于timelocal,即将tm格式,转成秒数。

strftime和strptime

两函数原型为:
size_t strftime(char *s, size_t max, const char *format, const struct tm *tm);
char *strptime(const char *s, const char *format, struct tm *tm);


上面两个函数也是互逆的,前者进行格式化,后者进行反格式化。特别注意,使用strftime前应该将tm进行memset清零,因为strftime不会帮你把未使用的变量清零,使用举例如下:
const char *date = "2014-01-16";

struct tm my_tm;
memset(&my_tm, 0, sizeof(my_tm));
strptime(date, "%Y-%m-%d", &my_tm);


localtime_r和gmtime_r性能对比

最后附后上一段测试localtime_r和gmtime_r性能的代码:
int main()
{
char buf[26];
unsigned long long start_tick;
struct tm gmtm;
struct tm lotm;

time_t now_timestamp = time(NULL);
printf("%lu\n", now_timestamp);
ctime_r(&now_timestamp, buf);
printf("%s\n", buf);

start_tick = rdtsc();
localtime_r(&now_timestamp, &lotm);
printf("rdtsc: %llu\n", rdtsc() - start_tick);
asctime_r(&lotm, buf);
printf("localtime_r: %s\n", buf);
printf("%d-%d-%d %d:%d:%d %s %ld\n",
lotm.tm_year, lotm.tm_mon, lotm.tm_mday, lotm.tm_hour,
lotm.tm_min, lotm.tm_sec, lotm.tm_zone, lotm.tm_gmtoff);
printf("%lu\n", mktime(&lotm));

start_tick = rdtsc();
gmtime_r(&now_timestamp, &gmtm);
printf("rdtsc: %llu\n", rdtsc() - start_tick);
asctime_r(&gmtm, buf);
printf("gmtime_r: %s\n", buf);
printf("%d-%d-%d %d:%d:%d %s %ld\n",
gmtm.tm_year, gmtm.tm_mon, gmtm.tm_mday, gmtm.tm_hour,
gmtm.tm_min, gmtm.tm_sec, gmtm.tm_zone, gmtm.tm_gmtoff);
printf("%lu\n", mktime(&gmtm));

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: