本次将对日志进行一次优化,参考一篇Ring-Log日志库,基于双向、循环的链表的数据结构实现高效率、易拓展的日志库;
========》每秒百万级高效C++异步日志实践《========
1、优化策略
为了减少对localtime的调用,使用以下策略
RingLog使用变量_sys_acc_sec记录写上一条日志时,系统经过的秒数(从1970年起算)、使用变量_sys_acc_min记录写上一条
日志时,系统经过的分钟数,并缓存写上一条日志时的年月日时分秒year、mon、day、hour、min、sec,并缓存UTC日志格式字符
串;
每当准备写一条日志:
- 调用gettimeofday获取系统经过的秒tv.tv_sec,与_sys_acc_sec比较;
- 如果tv.tv_sec 与 _sys_acc_sec相等,说明此日志与上一条日志在同一秒内产生,故年月日时分秒是一样的,直接使用
缓存即可;
- 否则,说明此日志与上一条日志不在同一秒内产生,继续检查:tv.tv_sec/60即系统经过的分钟数与_sys_acc_min比较;
- 如果tv.tv_sec/60与_sys_acc_min相等,说明此日志与上一条日志在同一分钟内产生,故年月日时分是一样的,年月日时
分 使用缓存即可,而秒sec = tv.tv_sec%60,更新缓存的秒sec,重组UTC日志格式字符串的秒部分;
- 否则,说明此日志与上一条日志不在同一分钟内产生,调用localtime重新获取UTC时间,并更新缓存的年月日时分秒,重组
UTC日志格式字符串
2、简单测试
/*----------------------------------------------------------------------
> File Name: testClock.cpp
> Author: Jxiepc
> Mail: Jxiepc
> Created Time: Sun 24 Jul 2022 02:02:36 PM CST
----------------------------------------------------------------------*/
#include <iostream>
#include <time.h>
#include <sys/time.h>
using namespace std;
struct UtcTimer
{
UtcTimer()
{
struct timeval tv;
gettimeofday(&tv, NULL);
_sys_acc_sec = tv.tv_sec; // 秒
_sys_acc_min = _sys_acc_sec / 60; // 分
struct tm tm;
localtime_r((time_t*)&_sys_acc_sec, &tm);
year = tm.tm_year + 1900;
mon = tm.tm_mon + 1;
day = tm.tm_mday;
hour = tm.tm_hour;
min = tm.tm_min;
sec = tm.tm_sec;
reset_utc_fmt();
}
uint64_t get_curr_time(int* p_msec = NULL)
{
struct timeval tv;
gettimeofday(&tv, NULL);
if (p_msec)
*p_msec = tv.tv_usec / 1000;
if ((uint32_t)tv.tv_sec != _sys_acc_sec)
{
sec = tv.tv_sec % 60;
_sys_acc_sec = tv.tv_sec;
if (_sys_acc_sec / 60 != _sys_acc_min)
{
_sys_acc_min = _sys_acc_sec / 60;
struct tm cur_tm;
localtime_r((time_t*)&_sys_acc_sec, &cur_tm);
year = cur_tm.tm_year + 1900;
mon = cur_tm.tm_mon + 1;
day = cur_tm.tm_mday;
hour = cur_tm.tm_hour;
min = cur_tm.tm_min;
reset_utc_fmt();
}
else
{
reset_utc_fmt_sec();
}
}
return tv.tv_sec;
}
int year, mon, day, hour, min, sec;
char utc_fmt[20];
private:
void reset_utc_fmt()
{
snprintf(utc_fmt, 20, "%d-%02d-%02d %02d:%02d:%02d", year, mon, day, hour, min, sec);
}
void reset_utc_fmt_sec()
{
snprintf(utc_fmt + 17, 3, "%02d", sec);
}
uint64_t _sys_acc_min;
uint64_t _sys_acc_sec;
};
void func1() {
UtcTimer ut;
for(int i=0; i<1000000000; ++i)
ut.get_curr_time();
}
void func2() {
time_t _sys_acc_sec;
struct tm tm;
for(int i=0; i<1000000000; ++i)
localtime_r((time_t*)&_sys_acc_sec, &tm);
}
int main(int argc, char* argv[])
{
clock_t begin = clock();
func1();
clock_t end = clock();
cout << "tick=" << double(end - begin)/1000 << endl;
return 0;
}
3、继续优化
将结构体中的成员去除,用struct tm取代;
若秒数改变只需将tm的tm_sec修改即可;
/*----------------------------------------------------------------------
> File Name: testTm.cpp
> Author: Jxiepc
> Mail: Jxiepc
> Created Time: Sun 24 Jul 2022 10:28:22 PM CST
----------------------------------------------------------------------*/
#include <iostream>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>
using namespace std;
struct UtcTimer
{
UtcTimer()
{
struct timeval tv;
gettimeofday(&tv, NULL);
_sys_acc_sec = tv.tv_sec; // 秒
_sys_acc_min = _sys_acc_sec / 60; // 分
struct tm tm;
localtime_r((time_t*)&_sys_acc_sec, &tm);
year = tm.tm_year + 1900;
mon = tm.tm_mon + 1;
day = tm.tm_mday;
hour = tm.tm_hour;
min = tm.tm_min;
sec = tm.tm_sec;
reset_utc_fmt();
}
uint64_t get_curr_time(int* p_msec = NULL)
{
struct timeval tv;
gettimeofday(&tv, NULL);
if (p_msec)
*p_msec = tv.tv_usec / 1000;
if ((uint32_t)tv.tv_sec != _sys_acc_sec)
{
sec = tv.tv_sec % 60;
_sys_acc_sec = tv.tv_sec;
if (_sys_acc_sec / 60 != _sys_acc_min)
{
_sys_acc_min = _sys_acc_sec / 60;
struct tm cur_tm;
localtime_r((time_t*)&_sys_acc_sec, &cur_tm);
year = cur_tm.tm_year + 1900;
mon = cur_tm.tm_mon + 1;
day = cur_tm.tm_mday;
hour = cur_tm.tm_hour;
min = cur_tm.tm_min;
reset_utc_fmt();
}
else
{
reset_utc_fmt_sec();
}
}
return tv.tv_sec;
}
int year, mon, day, hour, min, sec;
char utc_fmt[20];
private:
void reset_utc_fmt()
{
snprintf(utc_fmt, 20, "%d-%02d-%02d %02d:%02d:%02d", year, mon, day, hour, min, sec);
}
void reset_utc_fmt_sec()
{
snprintf(utc_fmt + 17, 3, "%02d", sec);
}
uint64_t _sys_acc_min;
uint64_t _sys_acc_sec;
};
struct testTimer {
testTimer() {
time_t t = time(0);
localtime_r(&t, &tm);
}
void listen() {
time_t t = time(0);
if(tm.tm_sec != t % 60) {
if(tm.tm_min == t / 60 % 60) {
tm.tm_sec = t % 60;
}else {
localtime_r(&t, &tm);
}
}
}
void p() {
std::cout << tm.tm_sec << ":" << tm.tm_min << std::endl;
}
struct tm tm;
};
void func1() {
UtcTimer ut;
for(int i=0; i<100000000; ++i) {
ut.get_curr_time();
}
}
void func5() {
testTimer ut;
for(int i=0; i<100000000; ++i) {
ut.listen();
}
}
int main(int argc, char* argv[])
{
clock_t begin = clock();
func1();
clock_t end = clock();
cout << "tick=" << double(end - begin)/CLOCKS_PER_SEC << "秒" << endl;
begin = clock();
func5();
end = clock();
cout << "tick=" << double(end - begin)/CLOCKS_PER_SEC << "秒"<< endl;
return 0;
}
4、嵌入log