在unix/win32上高效计算用于日志记录的日期/时间戳



在分析和分析了我们的系统后,我得出的结论是,系统的日志记录组件是许多瓶颈之一,约占总运行时间的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> ...

在我看来,你有两个问题:

  1. 将时间戳与其他系统同步
  2. 在单个系统上获取准确的时间戳

我会使用基于计时器的系统,每毫秒更新两次时间戳,并在更新之间重复使用。然后我会确保代码运行的系统的时钟与原子钟同步。您生成两次时间戳,试图补偿底层操作系统计时器机制的不稳定。

我不认为你能做得比这更好。

编辑:事实上,你可以。请确保只在时间戳字符串更改时对其进行格式化。如果你能保证条目按顺序记录,你也不需要序列号。考虑到这两个假设,你的日志记录问题现在可以归结为连接和写出两个字符串的速度。

更新2:如果BOOST不合适,如果你不能使用C++11,那么它可以归结为:

  1. 使用计时器每毫秒设置和格式化两次时间戳-您可以通过操作系统级别的API来实现这一点
  2. 请确保按事件出现的顺序记录事件

假设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运算,以获得正确的偏移量。

如果您可以用二进制格式记录时间戳,那么就可以解决格式问题。

最新更新