项目中使用发现mktime开销较大,使用性能测试工具测试一下;
主要用于所指向的结构转换为自 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.1‐2017 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成员与系统的是否一致。
结合另一篇文章的思路 《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.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?)