(gdb) bt
#0 __localtime_r (t=0xb7376880, tp=0xb737684c) at localtime.c:33
#1 0xb74f12db in ranged_convert (convert=<optimized out>, t=<optimized out>, tp=0xb737684c) at mktime.c:233
#2 0xb74f159c in __mktime_internal (tp=0xb73768f4, convert=0xb74f1280 <__localtime_r>, offset=0xb75c0a74) at mktime.c:405
#3 0xb74f1ad6 in *__GI_mktime (tp=0xb73768f4) at mktime.c:518
#4 0xaf8ffa40 in make_utc_time (gmt_tm=0xb73768f4) at /home/bright/inficore/service/adapter/smpp/server_submit_sm.cpp:96
glibc的对应代码
https://github.com/Xilinx/glibc/blob/9a3c6a6ff602c88d7155139a7d7d0000b7b7e946/time/mktime.c
http://code.woboq.org/userspace/glibc/time/localtime.c.html
-------------测试代码--------------------------
#include <fstream>
#include <iostream>
#include <map>
#include <sstream>
#include <list>
#include <vector>
#include <random>
//
//#include <boost/shared_ptr.hpp>
//#include <boost/weak_ptr.hpp>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/timeb.h>
#include <time.h>
#include "pugixml.hpp"
#ifdef _WIN32
#include <Windows.h>
void my_gettimeofday(struct timeval *tp)
{
uint64_t intervals;
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
/*
* A file time is a 64-bit value that represents the number
* of 100-nanosecond intervals that have elapsed since
* January 1, 1601 12:00 A.M. UTC.
*
* Between January 1, 1970 (Epoch) and January 1, 1601 there were
* 134744 days,
* 11644473600 seconds or
* 11644473600,000,000,0 100-nanosecond intervals.
*
* See also MSKB Q167296.
*/
intervals = ((uint64_t)ft.dwHighDateTime << 32) | ft.dwLowDateTime;
intervals -= 116444736000000000;
tp->tv_sec = (long)(intervals / 10000000);
tp->tv_usec = (long)((intervals % 10000000) / 10);
}
#else
#include <sys/time.h>
#define my_gettimeofday(tp) (void) gettimeofday(tp, NULL);
#endif
// 返回微秒时间差异
unsigned long long time_stamp_usec()
{
enum { kUsecPerSec = 1000 * 1000 };
struct timeval tp;
my_gettimeofday(&tp);
return ((unsigned long long)tp.tv_sec) * kUsecPerSec + (unsigned long long ) tp.tv_usec;
}
void print_time_diff(unsigned long long t0, unsigned long long t1, int n)
{
std::cout << "执行次数(n):\t" << n << std::endl
<< "总耗时(微秒):\t " << t1 - t0 << std::endl
<< "平均耗时(微秒)" << (double)(t1 - t0)/(double)n << std::endl;
}
using namespace std;
int main(int, char**)
{
struct tm time;
int seconds = 0;
std::cin >> seconds;
time.tm_year = 114;
time.tm_mon = 4;
time.tm_mday = 29;
time.tm_hour = 11;
time.tm_min = 10;
time.tm_sec = seconds;
//-----------------------
stringstream sstream;
unsigned long long total_counter = 0;
int kLoopCount = 10000 * 10000;
int i, j;
unsigned long long t0, t1;
//----------------------------------
t0 = time_stamp_usec();
for (i = 0; i < kLoopCount; i++) {
time.tm_sec = (time.tm_sec + 1) % 60;
time_t t = mktime(&time);
total_counter += t;
}
t1 = time_stamp_usec();
print_time_diff(t0, t1, kLoopCount);
//----------------------------------
std::cout << total_counter << std::endl;
int aaa;
cin >> aaa;
cout << aaa;
return 0;
}
----------------------用mktime模拟 timegm或者_mkgmtime 功能的代码--------------------
class TimezoneHelper {
public:
TimezoneHelper() {
struct tm * timeinfo;
time_t secs, local_secs, gmt_secs;
time(&secs);
timeinfo = localtime(&secs);
local_secs = mktime(timeinfo);
timeinfo = gmtime(&secs);
gmt_secs = mktime(timeinfo);
timezone_diff_secs = local_secs - gmt_secs;
}
static long timezone_diff_secs;
};
long TimezoneHelper::timezone_diff_secs = 0;
static TimezoneHelper timezone_helper;
inline time_t make_utc_time(struct tm *gmt_tm)
{
time_t t = mktime(gmt_tm) + TimezoneHelper::timezone_diff_secs;
return t;
}
等价于
#ifdef _WIN32
#define make_utc_time _mkgmtime
#else
#define make_utc_time timegm
#endif
-------------windows 7 64bit vc 2013
执行次数(n): 100000000
总耗时(微秒): 19797514
平均耗时(微秒)0.197975
因为是编译的32位程序,所以用32bit运算模拟的除法要慢一些。
如果定义宏#define _USE_32BIT_TIME_T 来使用32位的time_t
测试结果如下:
执行次数(n): 100000000
总耗时(微秒): 9805745
平均耗时(微秒)0.0980575
要比64bit的time_t 时候要快一倍左右。
----------------windows 平台的_mkgmtime -----------
执行次数(n): 100000000
总耗时(微秒): 6615840
平均耗时(微秒)0.0661584
这个也要比直接用mktime要快。
----------virtualbox 4.3.10 linux -----------
bright@debian01:~/test$ uname -a
Linux debian01 3.11-0.bpo.2-686-pae #1 SMP Debian 3.11.10-1~bpo70+1 (2013-12-17) i686 GNU/Linux
bright@debian01:~/test$ gcc --version
gcc (Debian 4.7.2-5) 4.7.2
bright@debian01:~/test$ make test
g++ -g -std=c++11 -O2 -o mktime_test -lrt main.c
执行次数(n): 100000000
总耗时(微秒): 97892366
平均耗时(微秒)0.978924
要比Windows的32位time_t慢10倍左右
---------------------------------------------------
如果改用timegm函数(不是posix标准,但glibc的也有实现bsd接口)
参考
http://linux.die.net/man/3/timegm
执行次数(n): 100000000
总耗时(微秒): 8867971
平均耗时(微秒)0.0886797
timegm要比mktime要快好十倍的样子,比windows的mktime要快一点,但比Windows平台的_mkgmtime 要慢一些。
---------------------linux mktime的perf top 分析结果--------------------------------
- 33.98% libc-2.13.so [.] _IO_vfscanf
- _IO_vfscanf
- 99.49% _IO_vsscanf
sscanf
__tzset_parse_tz
__tzfile_compute
__tz_convert
__localtime_r
ranged_convert
__mktime_internal
mktime
main
__libc_start_main
_start
- 0.51% sscanf
__tzset_parse_tz
__tzfile_compute
__tz_convert
__localtime_r
ranged_convert
__mktime_internal
mktime
main
__libc_start_main
_start
+ 7.52% libc-2.13.so [.] __offtime
+ 5.49% libc-2.13.so [.] __mktime_internal
----------------linux timegm的perf top分析-------------------------------------
- 41.58% libc-2.13.so [.] __mktime_internal
__mktime_internal
- 28.43% libc-2.13.so [.] __offtime
- __offtime
+ 98.65% __tz_convert
+ 1.35% __gmtime_r
- 8.27% libc-2.13.so [.] __tz_convert
- __tz_convert
+ 95.66% __gmtime_r
+ 4.34% ranged_convert
- 7.26% libc-2.13.so [.] __tzfile_compute
+ __tzfile_compute
+ 3.36% libc-2.13.so [.] __i686.get_pc_thunk.bx
------------------------------------------------------------------
总结:
linux 的mktime要比windows的要慢 好几倍倍,
应该是函数开始的地方调用__tzset 函数来更新timezone设置或者scanf解析timezone字符串导致导致的吧?
但timegm函数和windows的 mktime就差不多了。Linux平台的timelocal函数应该等价于mktime,跟mktime一样的慢。
timegm比mktime应该就是少了一个解析timezone文件的操作。
看来可以用timegm的地方尽量不要用mktime吧。但timegm不是标准函数,之前代码里面都是用 mktime来模拟timegm的功能,自己加上localtime_offset的timezone偏移值。看来还是直接用timegm要好一些吧。
类似的有Linux的localtime要比gmtime要慢好多倍
------------windows gmtime----------
执行次数(n): 100000000
总耗时(微秒): 4909623
平均耗时(微秒)0.0490962
------------windows localtime-------
执行次数(n): 100000000
总耗时(微秒): 7635470
平均耗时(微秒)0.0763547
------------linux gmtime----------
执行次数(n): 100000000
总耗时(微秒): 4274841
平均耗时(微秒)0.0427484
------------linux locatime-----------
执行次数(n): 100000000
总耗时(微秒): 92113588
平均耗时(微秒)0.921136
类似的Linux平台的gmtime函数要比locatime要快很多啊。如果用的比较多的时候,可以自己获取一次timezone的秒数偏移值。然后以后都用gmtime + timezone_diff_secs来模拟locatime函数吧。像localtime这样每次去获取解析timezone确实没必要。cache一次就可以了。它的实现估计是为了兼容posix的标准,要求每次调用时都去重新获取timezone。但一般来说timezone都不会变的吧。
struct tm * timeinfo;
time_t secs, local_secs, gmt_secs;
time(&secs);
timeinfo = localtime(&secs);
local_secs = mktime(timeinfo);
timeinfo = gmtime(&secs);
gmt_secs = mktime(timeinfo);
timezone_diff_secs = local_secs - gmt_secs;
设置要timezone计算的函数都会慢一些,如果可以的话直接使用UTC时间函数吧,有可能系统内部保存的就是UTC时间,不需要再做timezone的换算了。