测试mktime和localtime_r性能及优化方法

// 编译方法:g++ -g -o x x.cpp或g++ -O2 -o x x.cpp,两种编译方式性能基本相同。
//
// 结论:
// 1) 环境变量TZ和isdst均不影响localtime_r的性能
// 2) 环境变量TZ严重影响mktime和localtime的性能
// 3) mktime性能不受isdst值的影响,localtime性能与isdst值无关
// *4) 另外需要注意localtime_r为非信号安全函数,
// 不能在信号处理过程中调用,否则可能发生死锁等问题
//
// 64位机器性能数据(与32位CPU不同):
/*
$ ./x 1000000
test: localtime ...
TZ is NULL: 2629ms
TZ is empty: 177ms
TZ is Asia/Shanghai: 177ms

test: localtime_r ...
TZ is NULL and isdst=1: 124ms
TZ is NULL and isdst=0: 125ms
TZ is NULL and isdst=-1: 124ms
TZ is NULL and isdst undefined: 124ms
TZ is empty and isdst=1: 124ms
TZ is empty and isdst=0: 124ms
TZ is empty and isdst=-1: 124ms
TZ is empty and isdst undefined: 124ms
TZ is Asia/Shanghai and isdst=1: 124ms
TZ is Asia/Shanghai and isdst=0: 125ms
TZ is Asia/Shanghai and isdst=-1: 124ms
TZ is Asia/Shanghai and isdst undefined: 124ms

test: mktime ...
TZ is NULL and isdst=1: 2657ms
TZ is NULL and isdst=0: 2654ms
TZ is NULL and isdst=-1: 2661ms
TZ is NULL and isdst undefined: 2672ms
TZ is empty and isdst=1: 232ms
TZ is empty and isdst=0: 232ms
TZ is empty and isdst=-1: 232ms
TZ is empty and isdst undefined: 230ms
TZ is Asia/Shanghai and isdst=1: 233ms
TZ is Asia/Shanghai and isdst=0: 231ms
TZ is Asia/Shanghai and isdst=-1: 232ms
TZ is Asia/Shanghai and isdst undefined: 231ms
*/
// 32位机器性能数据(与64位CPU不同):
/*
> ./x 1000000
test: localtime ...
TZ is NULL: 1510ms
TZ is empty: 252ms
TZ is Asia/Shanghai: 255ms

test: localtime_r ...
TZ is NULL and isdst=1: 157ms
TZ is NULL and isdst=0: 159ms
TZ is NULL and isdst=-1: 159ms
TZ is NULL and isdst undefined: 158ms
TZ is empty and isdst=1: 158ms
TZ is empty and isdst=0: 160ms
TZ is empty and isdst=-1: 157ms
TZ is empty and isdst undefined: 158ms
TZ is Asia/Shanghai and isdst=1: 157ms
TZ is Asia/Shanghai and isdst=0: 156ms
TZ is Asia/Shanghai and isdst=-1: 156ms
TZ is Asia/Shanghai and isdst undefined: 157ms

test: mktime ...
TZ is NULL and isdst=1: 1542ms
TZ is NULL and isdst=0: 1547ms
TZ is NULL and isdst=-1: 1542ms
TZ is NULL and isdst undefined: 1566ms
TZ is empty and isdst=1: 327ms
TZ is empty and isdst=0: 324ms
TZ is empty and isdst=-1: 325ms
TZ is empty and isdst undefined: 323ms
TZ is Asia/Shanghai and isdst=1: 323ms
TZ is Asia/Shanghai and isdst=0: 326ms
TZ is Asia/Shanghai and isdst=-1: 326ms
TZ is Asia/Shanghai and isdst undefined: 324ms
*/
// localtime_r相关源代码:
/*
// The C Standard says that localtime and gmtime return the same pointer.
struct tm _tmbuf; // 全局变量

struct tm * __localtime_r (t, tp)
     const time_t *t;
     struct tm *tp;
{
  return __tz_convert (t, 1, tp);
}

// 非线程安全版本,用到了全局变量_tmbuf
struct tm * localtime(t)
     const time_t *t;
{
  return __tz_convert (t, 1, &_tmbuf);
}

struct tm * __tz_convert (const time_t *timer, int use_localtime, struct tm *tp)
{
  。。。
  // 信号处理函数中调用非信号安全函数,可能造成死锁的地方
  __libc_lock_lock (tzset_lock);
  
  // localtime_r未用到_tmbuf,只是localtime使用它!!!
  // 因此对于localtime_r,传递给tzset_internal的第一个参数总是为0(tp != &_tmpbuf),
  // 而对于localtime,它传递给tzset_internal的第一个参数总是为1
  tzset_internal (tp == &_tmbuf && use_localtime, 1);
  。。。
}

// 决定性能的函数,原因是可能涉及文件操作,
// 因此要想提升性能,则应当想办法避免操作文件!!!
static void internal_function
tzset_internal (always, explicit)
     int always;
     int explicit;
{
  static int is_initialized; // 静态变量
  const char *tz;

  // 对于mktime,参数always值总是为1
  // 对于localtime,参数always值总是为1
  // 对于localtime_r,参数always值总是为0 
  if (is_initialized && !always)
    return; // 对于localtime_r第一次调用后,后续都在这里直接返回!
  is_initialized = 1;

  tz = getenv ("TZ");
  if (tz == NULL && !explicit)
    tz = TZDEFAULT;
  if (tz && *tz == '\0')
    tz = "Universal";
  if (tz && *tz == ':')
    ++tz;

  // 如果不设置环境变量TZ,则下面这个if语句总是不成立!!!
  // 因此只有设置了环境变量TZ,才有可能在这里直接返回而不进入读文件操作__tzfile_read
  if (old_tz != NULL && tz != NULL && strcmp (tz, old_tz) == 0)
    return; // 在这里返回则可以避免走到文件操作__tzfile_read
  if (tz == NULL)
    tz = TZDEFAULT;

  tz_rules[0].name = NULL;
  tz_rules[1].name = NULL;

  // Save the value of `tz'.
  free (old_tz);
  old_tz = tz ? __strdup (tz) : NULL;

  // 读文件,性能慢的原因 
  __tzfile_read (tz, 0, NULL); // Try to read a data file.
  if (__use_tzfile)
    return;
  。。。
}
*/
// mktime相关源代码:
/*
time_t mktime (struct tm *tp)
{
#ifdef _LIBC
  // POSIX.1 8.1.1 requires that whenever mktime() is called, the
  // time zone names contained in the external variable 'tzname' shall
  // be set as if the tzset() function had been called.
  __tzset ();
#endif

  // __mktime_internal为全内存计算,因此无性能问题
  return __mktime_internal (tp, __localtime_r, &localtime_offset);
}

void __tzset (void)
{
  __libc_lock_lock (tzset_lock);

  // 和localtime_r一样也会调用tzset_internal
  tzset_internal (1, 1);

  if (!__use_tzfile)
    {
      // Set `tzname'.
      __tzname[0] = (char *) tz_rules[0].name;
      __tzname[1] = (char *) tz_rules[1].name;
    }

  __libc_lock_unlock (tzset_lock);
}
*/
#include <stdio.h>
#include <sys/time.h>
#include <time.h>
#include <stdlib.h>

static void test_localtime(int M); // 测试localtime性能
static void test_localtime_r(int M); // 测试localtime_r性能
static void test_mktime(int M); // 测试mktime性能

int main(int argc, char* argv[])
{
    const int M = (argc<2)? 1000000: atoi(argv[1]);

    test_localtime(M);
    printf("\n");
    test_localtime_r(M);
    printf("\n");
    test_mktime(M);

    return 0;
}

// test_localtime
void test_localtime(int M)
{
    int i;
    time_t now = time(NULL);
    struct timeval tv1, tv2;

    printf("test: localtime ...\n");
    unsetenv("TZ");

    // test1
    { 
        struct tm* result1; 
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            result1 = localtime(&now);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is NULL: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }
    
    setenv("TZ", "", 0);

    // test2
    { 
        struct tm* result2; 
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            result2 = localtime(&now);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is empty: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    setenv("TZ", "Asia/Shanghai", 0);

    // test3
    { 
        struct tm* result3;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            result3 = localtime(&now);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is Asia/Shanghai: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }
}

// test_localtime_r
void test_localtime_r(int M)
{
    int i;
    time_t now = time(NULL);
    struct timeval tv1, tv2;
  
    printf("test: localtime_r ...\n");
    unsetenv("TZ");

    // test1
    { 
        struct tm result1; 
        result1.tm_isdst = 1;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            localtime_r(&now, &result1);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is NULL and isdst=1: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    // test2
    {
        struct tm result2;
        result2.tm_isdst = 0;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            localtime_r(&now, &result2);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is NULL and isdst=0: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    // test3
    {
        struct tm result3;
        result3.tm_isdst = -1;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            localtime_r(&now, &result3);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is NULL and isdst=-1: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    // test4
    {
        struct tm result4;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            localtime_r(&now, &result4);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is NULL and isdst undefined: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }
    
    setenv("TZ", "", 0);

    // test5
    { 
        struct tm result5; 
        result5.tm_isdst = 1;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            localtime_r(&now, &result5);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is empty and isdst=1: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    // test6
    {
        struct tm result6;
        result6.tm_isdst = 0;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            localtime_r(&now, &result6);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is empty and isdst=0: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    // test7
    {
        struct tm result7;
        result7.tm_isdst = -1;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            localtime_r(&now, &result7);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is empty and isdst=-1: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    // test8
    {
        struct tm result8;
        result8.tm_isdst = -1;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            localtime_r(&now, &result8);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is empty and isdst undefined: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    setenv("TZ", "Asia/Shanghai", 0);

    // test9
    { 
        struct tm result9; 
        result9.tm_isdst = 1;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            localtime_r(&now, &result9);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is Asia/Shanghai and isdst=1: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    // test10
    {
        struct tm result10;
        result10.tm_isdst = 0;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            localtime_r(&now, &result10);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is Asia/Shanghai and isdst=0: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    // test11
    {
        struct tm result11;
        result11.tm_isdst = -1;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            localtime_r(&now, &result11);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is Asia/Shanghai and isdst=-1: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    // test12
    {
        struct tm result12;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            localtime_r(&now, &result12);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is Asia/Shanghai and isdst undefined: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }
}

// test_mktime
void test_mktime(int M)
{
    int i; 
    time_t now = time(NULL);
    struct timeval tv1, tv2;

    printf("test: mktime ...\n");
    unsetenv("TZ");

    // test1
    { 
        struct tm result1;
        localtime_r(&now, &result1); 
        result1.tm_isdst = 1;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            mktime(&result1);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is NULL and isdst=1: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    // test2
    {
        struct tm result2;
        localtime_r(&now, &result2);
        result2.tm_isdst = 0;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            mktime(&result2);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is NULL and isdst=0: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    // test3
    {
        struct tm result3;
        localtime_r(&now, &result3);
        result3.tm_isdst = -1;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            mktime(&result3);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is NULL and isdst=-1: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    // test4
    {
        struct tm result4;
        localtime_r(&now, &result4);
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            mktime(&result4);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is NULL and isdst undefined: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    setenv("TZ", "", 0);

    // test5
    {
        struct tm result5;
        localtime_r(&now, &result5); 
        result5.tm_isdst = 1;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            mktime(&result5);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is empty and isdst=1: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    // test6
    {
        struct tm result6;
        localtime_r(&now, &result6);
        result6.tm_isdst = 0;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            mktime(&result6);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is empty and isdst=0: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    // test7
    {
        struct tm result7;
        localtime_r(&now, &result7);
        result7.tm_isdst = -1;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            mktime(&result7);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is empty and isdst=-1: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    // test8
    {
        struct tm result8;
        localtime_r(&now, &result8);
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            mktime(&result8);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is empty and isdst undefined: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    setenv("TZ", "Asia/Shanghai", 0);

    // test9
    {
        struct tm result9;
        localtime_r(&now, &result9); 
        result9.tm_isdst = 1;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            mktime(&result9);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is Asia/Shanghai and isdst=1: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    // test10
    {
        struct tm result10;
        localtime_r(&now, &result10);
        result10.tm_isdst = 0;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            mktime(&result10);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is Asia/Shanghai and isdst=0: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    // test11
    {
        struct tm result11;
        localtime_r(&now, &result11);
        result11.tm_isdst = -1;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            mktime(&result11);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is Asia/Shanghai and isdst=-1: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    // test12
    {
        struct tm result12;
        localtime_r(&now, &result12);
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            mktime(&result12);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is Asia/Shanghai and isdst undefined: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏禅林阆苑

mysql 命令完全总结 【原创】

mysql 命令完全总结 Write By CS逍遥剑仙 我的主页: www.csxiaoyao.com GitHub: github.com...

350110
来自专栏java一日一条

MySQL处理数据库和表的常用命令

学习如何管理和导航MySQL数据库和表是要掌握的首要任务之一,下面的内容将主要对MySQL的数据库和表的一些常用命令进行总结,一些我们不得不掌握的命令,一些信手...

11430
来自专栏PhpZendo

MySQL创建用户与授权

假如你在给用户'pig'@'%'授权的时候是这样的(或类似的):GRANT SELECT ON test.user TO 'pig'@'%',则在使用REVOK...

55510
来自专栏源哥的专栏

oracle中如何删除重复数据

        我们可能会出现这种情况,某个表原来设计不周全,导致表里面的数据数据重复,那么,如何对重复的数据进行删除呢?         重复的数据可能有这样...

14430
来自专栏Java学习123

Linux各类压宿包的解压方法

29290
来自专栏Aox Lei

Mycat基本使用教程

路径在: /usr/local/mycat/conf/rule.xml function必须在tableRule的下面, 否则会报错找不到

51820
来自专栏python3

mysql-数据库的操作

nformation_schema: 虚拟库,不占用磁盘空间,存储的是数据库启动后的一些参数,如用户表信息、列信息、权限信息、字符信息等

11320
来自专栏乐沙弥的世界

导出MySQL用户权限

    在对MySQL数据库进行迁移的时候,有时候也需要迁移源数据库内的用户与权限。对于这个迁移我们可以从mysql.user表来获取用户的相关权限来生成相应的...

17630
来自专栏Albert陈凯

SQL语句各个部分执行顺序

http://www.tuicool.com/articles/fERNv2 下面先给出上面的SQL语句的执行顺序,然后进行讲解: (8)  select (...

32970
来自专栏csxiaoyao

mysql 命令完全总结

45470

扫码关注云+社区

领取腾讯云代金券