在分析和分析了我们的系统后,我得出的结论是,系统的日志记录组件是许多瓶颈之一,约占总运行时间的17%,很多事情都被记录了下来。
其中,记录器消耗的大约5%的时间与生成以下格式的ascii日期/时间戳有关:YYYYMMDD HHMMSS.fff-我们大约每秒记录大约700k行。(大约每秒700K次(本地时间和gettimeofday)呼叫)
我想知道同事们有什么技术可以有效地制作时间戳。
跨平台解决方案将受到欢迎。
注1:我们研究了Boost.datetime-这很好,但对我们的需求来说有点太慢了,std::chrono是一个完美的解决方案,但不幸的是,我们必须支持c++11之前的编译器。
注2:我们实现了一个简单的优化,每24小时只计算一次日期部分(yyyymmdd),因此每行只有1次gettimeofday调用,但没有多大帮助。
如果您可以选择使用C++11,您应该检查std::chrono。
否则,优化将取决于您需要的分辨率。我想问一下,您是否绝对需要日志记录上的时间戳,或者带有序列信息的偶尔时间戳是否有用?
示例:
<timestamp1> <seq_num_0> ...
<timestamp1> <seq_num_1> ...
....
<timestamp1> <seq_num_n-1> ...
<timestamp2> <seq_num_0> ...
在我看来,你有两个问题:
- 将时间戳与其他系统同步
- 在单个系统上获取准确的时间戳
我会使用基于计时器的系统,每毫秒更新两次时间戳,并在更新之间重复使用。然后我会确保代码运行的系统的时钟与原子钟同步。您生成两次时间戳,试图补偿底层操作系统计时器机制的不稳定。
我不认为你能做得比这更好。
编辑:事实上,你可以。请确保只在时间戳字符串更改时对其进行格式化。如果你能保证条目按顺序记录,你也不需要序列号。考虑到这两个假设,你的日志记录问题现在可以归结为连接和写出两个字符串的速度。
更新2:如果BOOST不合适,如果你不能使用C++11,那么它可以归结为:
- 使用计时器每毫秒设置和格式化两次时间戳-您可以通过操作系统级别的API来实现这一点
- 请确保按事件出现的顺序记录事件
假设I/O不是您的瓶颈,那么您的问题只不过是快速字符串串联。
我会延迟任何和所有格式化,直到真正需要:
struct log_entry {
struct timeval timestamp;
unsigned int code;
union {
struct param1 p1;
struct param2 p2;
};
};
paramN
结构包含适用于事件的数据,无论它们当时处于何种形式,但作为副本(因此可以独立分析日志数据)。
根据您的要求,您可以将这些数据保存在环形缓冲区中,并不断覆盖旧数据,或者在达到一定百分比时将其转储到磁盘。
编辑:现在有多个向下投票者。请留下评论,这样我就可以正确地解决这些问题。谢谢
您可以重新组织代码,以便记录器从缓冲区读取日期时间戳字符串,该字符串由其他线程每秒更新N次(取决于您想要的分辨率)。每秒4次:
struct current_time_stamp {
char timestr_[4][16];
unsigned index_;
unsigned subsecond_;
const char *get () const { return timestr_[index_%4]; }
void update () {
// ... update string in timestr_[(index_+1)%4] ...
// ... if (index_ + 1)%4 is zero, recompute subsecond_
ATOMIC_INCREMENT(index_);
// ... also need a memory barrier for timestr_ update
}
};
每个日志的亚秒分辨率将从高性能计数器中读取。DeadMG建议在windows上使用QueryPerformanceTimer
,而在Linux(和POSIX)上则使用clock_gettime
。但是,如果这些实现的开销仍然很大,则可以直接使用内联汇编查询处理器上的时间戳计数器(请参阅x86的rdtsc
)。亚秒值与结构中记录的值进行delta运算,以获得正确的偏移量。
如果您可以用二进制格式记录时间戳,那么就可以解决格式问题。