Linux下mktime接口耗时测试

1、背景

项目中使用发现mktime开销较大,使用性能测试工具测试一下;

2、接口说明

主要用于所指向的结构转换为自 1970 年1月1日以来持续时间的秒数,发生错误时返回-1。

SYNOPSIS         top
       #include 

       time_t mktime(struct tm *timeptr);
DESCRIPTION
       The functionality described on this reference page is aligned
       with the ISO C standard. Any conflict between the requirements
       described here and the ISO C standard is unintentional. This
       volume of POSIX.12017 defers to the ISO C standard.

       The mktime() function shall convert the broken-down time,
       expressed as local time, in the structure pointed to by timeptr,
       into a time since the Epoch value with the same encoding as that
       of the values returned by time().  The original values of the
       tm_wday and tm_yday components of the structure shall be ignored,
       and the original values of the other components shall not be
       restricted to the ranges described in <time.h>.
---
       Broken-down time is stored in the structure tm, which is defined in <time.h> as follows:

           struct tm {
               int tm_sec;    /* Seconds (0-60) */
               int tm_min;    /* Minutes (0-59) */
               int tm_hour;   /* Hours (0-23) */
               int tm_mday;   /* Day of the month (1-31) */
               int tm_mon;    /* Month (0-11) */
               int tm_year;   /* Year - 1900 */
               int tm_wday;   /* Day of the week (0-6, Sunday = 0) */
               int tm_yday;   /* Day in the year (0-365, 1 Jan = 0) */
               int tm_isdst;  /* Daylight saving time */
           };

网上查了一些资料,这篇文章通过源码分析得出结论:

《mktime性能分析》
从mktime的源码实现中可以看出,mktime的大致执行流程如下:
首先通过调用__tzset()对时区进行设置
若TZ环境变量为NULL,尝试调用__tzfile_read读取文件中的时区信息
只需要首次设置,若改变或为NULL重新设置
然后调用__mktime_internal进行时间转换
检查系统的tm_isdst(是否为夏令时)和传入的struct tm的tm_isdst,若两个不一致(isdst_differ),则进行矫正tm_isdst
从上源码可以得出结论:
影响mktime性能的两个因素主要包括两方面:
一是TZ设置是否为NULL,
二是传入的struct tm参数的tm_isdst成员与系统的是否一致。

3、性能测试

结合另一篇文章的思路 《mktime性能测试》
使用Google-benchmark框架自己再实现一下,首先实现基础类:

#include 
#include 

#include 
#include 

class bm_mktime:
    public ::benchmark::Fixture
{
public:
    time_t _now;
    struct tm _tm;

    bm_mktime()
    {
        setenv("TZ", "", 0);
        setenv("TZ", "Asia/Shanghai", 0);
        unsetenv("TZ");
    }

    void SetUp(const ::benchmark::State &st)
    {
        _now = time(NULL);
        (void)localtime_r(&_now, &_tm);
    }

    void TearDown(const ::benchmark::State &)
    {
    }   
};  

localtime接口测试

//
// localtime
//
BENCHMARK_DEFINE_F(bm_mktime, localtime_TZ_null)(benchmark::State &state)
{
    unsetenv("TZ");
    for (auto _ : state) {
        benchmark::DoNotOptimize(localtime(&_now));
    }
}

BENCHMARK_DEFINE_F(bm_mktime, localtime_TZ_empty)(benchmark::State &state)
{
    setenv("TZ", "", 0);
    for (auto _ : state) {
        benchmark::DoNotOptimize(localtime(&_now));
    }
}

BENCHMARK_DEFINE_F(bm_mktime, localtime_TZ_shanghai)(benchmark::State &state)
{
    setenv("TZ", "Asia/Shanghai", 0);
    for (auto _ : state) {
        benchmark::DoNotOptimize(localtime(&_now));
    }
}

localtime_r接口测试,带时区与不带时区:

//
// localtime_r
//

BENCHMARK_DEFINE_F(bm_mktime, localtime_r_TZ_NULL_isDST)(benchmark::State &state)
{
    unsetenv("TZ");
    _tm.tm_isdst = 1;
    for (auto _ : state) {
        benchmark::DoNotOptimize(localtime_r(&_now, &_tm));
    }
}

BENCHMARK_DEFINE_F(bm_mktime, localtime_r_TZ_NULL_noDST)(benchmark::State &state)
{
    unsetenv("TZ");
    _tm.tm_isdst = 0;
    for (auto _ : state) {
        benchmark::DoNotOptimize(localtime_r(&_now, &_tm));
    }
}

BENCHMARK_DEFINE_F(bm_mktime, localtime_r_TZ_empty_isDST)(benchmark::State &state)
{
    setenv("TZ", "", 0);
    _tm.tm_isdst = 1;
    for (auto _ : state) {
        benchmark::DoNotOptimize(localtime_r(&_now, &_tm));
    }
}

BENCHMARK_DEFINE_F(bm_mktime, localtime_r_TZ_empty_noDST)(benchmark::State &state)
{
    setenv("TZ", "", 0);
    _tm.tm_isdst = 0;
    for (auto _ : state) {
        benchmark::DoNotOptimize(localtime_r(&_now, &_tm));
    }
}

BENCHMARK_DEFINE_F(bm_mktime, localtime_r_TZ_shanghai_isDST)(benchmark::State &state)
{
    setenv("TZ", "Asia/Shanghai", 0);
    _tm.tm_isdst = 1;
    for (auto _ : state) {
        benchmark::DoNotOptimize(localtime_r(&_now, &_tm));
    }
}

BENCHMARK_DEFINE_F(bm_mktime, localtime_r_TZ_shanghai_noDST)(benchmark::State &state)
{
    setenv("TZ", "Asia/Shanghai", 0);
    _tm.tm_isdst = 0;
    for (auto _ : state) {
        benchmark::DoNotOptimize(localtime_r(&_now, &_tm));
    }
}

mktime测试,带时区与不带时区

//
// mktime
//

BENCHMARK_DEFINE_F(bm_mktime, mktime_TZ_NULL_isDST)(benchmark::State &state)
{
    unsetenv("TZ");
    _tm.tm_isdst = 1;
    for (auto _ : state) {
        benchmark::DoNotOptimize(mktime(&_tm));
    }
}

BENCHMARK_DEFINE_F(bm_mktime, mktime_TZ_NULL_noDST)(benchmark::State &state)
{
    unsetenv("TZ");
    _tm.tm_isdst = 0;
    for (auto _ : state) {
        benchmark::DoNotOptimize(mktime(&_tm));
    }
}

BENCHMARK_DEFINE_F(bm_mktime, mktime_TZ_empty_isDST)(benchmark::State &state)
{
    setenv("TZ", "", 0);
    _tm.tm_isdst = 1;
    for (auto _ : state) {
        benchmark::DoNotOptimize(mktime(&_tm));
    }
}

BENCHMARK_DEFINE_F(bm_mktime, mktime_TZ_empty_noDST)(benchmark::State &state)
{
    setenv("TZ", "", 0);
    _tm.tm_isdst = 0;
    for (auto _ : state) {
        benchmark::DoNotOptimize(mktime(&_tm));
    }
}

BENCHMARK_DEFINE_F(bm_mktime, mktime_TZ_shanghai_isDST)(benchmark::State &state)
{
    setenv("TZ", "Asia/Shanghai", 0);
    _tm.tm_isdst = 1;
    for (auto _ : state) {
        benchmark::DoNotOptimize(mktime(&_tm));
    }
}

BENCHMARK_DEFINE_F(bm_mktime, mktime_TZ_shanghai_noDST)(benchmark::State &state)
{
    setenv("TZ", "Asia/Shanghai", 0);
    _tm.tm_isdst = 0;
    for (auto _ : state) {
        benchmark::DoNotOptimize(mktime(&_tm));
    }
}

4、测试结论

测试机器信息
内核:4.12.7
glibc:2.26
CPU:Intel® Xeon® CPU E5-2630 v4 @ 2.20GHz

编译方法,需要依赖libbenchmark

g++ -std=c++11 -Wall -O3 -Os -o bm_mktime bm_mktime.cc  \
        -lbenchmark -lbenchmark_main -pthread

测试结果如下:

2021-09-11 10:56:51
Running ./bm_mktime
Run on (16 X 2199.23 MHz CPU s)
CPU Caches:
  L1 Data 32K (x4)
  L1 Instruction 32K (x4)
  L2 Unified 256K (x4)
  L3 Unified 25600K (x4)
Load Average: 0.00, 0.00, 0.00
----------------------------------------------------------------------------------
Benchmark                                        Time             CPU   Iterations
----------------------------------------------------------------------------------
bm_mktime/localtime_TZ_null                    836 ns          836 ns       832463
bm_mktime/localtime_TZ_empty                   354 ns          354 ns      1968125
bm_mktime/localtime_TZ_shanghai                347 ns          347 ns      2006657
bm_mktime/localtime_r_TZ_NULL_isDST            299 ns          299 ns      2303871
bm_mktime/localtime_r_TZ_NULL_noDST            303 ns          303 ns      2307305
bm_mktime/localtime_r_TZ_empty_isDST           301 ns          301 ns      2315543
bm_mktime/localtime_r_TZ_empty_noDST           300 ns          300 ns      2322080
bm_mktime/localtime_r_TZ_shanghai_isDST        302 ns          302 ns      2208106
bm_mktime/localtime_r_TZ_shanghai_noDST        298 ns          298 ns      2335372
bm_mktime/mktime_TZ_NULL_isDST                 921 ns          921 ns       755271
bm_mktime/mktime_TZ_NULL_noDST                 923 ns          923 ns       758570
bm_mktime/mktime_TZ_empty_isDST                419 ns          419 ns      1651223
bm_mktime/mktime_TZ_empty_noDST                419 ns          419 ns      1687080
bm_mktime/mktime_TZ_shanghai_isDST             419 ns          419 ns      1664563
bm_mktime/mktime_TZ_shanghai_noDST             417 ns          417 ns      1691596

目前结果看,夏令时DST设置与否没有什么大的影响,但是TZ不设置会有相关的损耗;
(Q1:什么场景下会使用DST?)

你可能感兴趣的:(linux,linux,测试工具)