简体   繁体   English

使用具有实时性能要求的C ++ Stl字符串流进行记录

[英]Logging using c++ stl stringstream with real-time performance requirements

I'm attempting to implement logging functionality in an application that is required to run in real-time (meaning, spending a few milliseconds writing to a file during execution of the main loop will significantly affect performance since we are interfacing with several systems that require low latency). 我正在尝试在需要实时运行的应用程序中实现日志记录功能(这意味着,在主循环执行过程中花费几毫秒写入文件会严重影响性能,因为我们正在与需要低延迟)。 My current implementation logs a line into a stringstream object each millisecond, and then writes the resulting string to a file only upon shutdown: 我当前的实现每毫秒将一行记录到stringstream对象中,然后仅在关机时将结果字符串写入文件:

class ControlDemo {
  public:
    ControlDemo();
    ~ControlDemo();
    void spin();
    // Other public methods
  private:
    void logLine(std::vector<double> data);
    void writeLogFile();
    std::stringstream m_logged_data;
    std::string m_log_filename;
    bool m_continue_spinning;
    // Other private methods
};

ControlDemo::~ControlDemo() {
  writeLogFile();  // write log data in RAM to a file
}

void ControlDemo::spin() {
  while(m_continue_spinning){
    // do stuff
    logLine(data_to_log);
    sleepRemainingTime();  // maintaining ~1ms loop rate
  }
}

void ControlDemo::logLine(std::vector<double> data) {
  m_logged_data << getCurrentTime();
  for (std::vector<double>::iterator it = data.begin(); it != data.end(); ++it)
    m_logged_data << ', ' << *it;
  m_logged_data << std::endl;
}

void ControlDemo::writeLogFile() {
  std::ofstream file;
  file.open(m_log_file_path.c_str());

  file << m_logged_data.str();
  file.close();
  std::cerr << "Wrote log to " << m_log_file_path << std::endl;
}

My desire is to implement some type of buffering functionality whereby I can specify the maximum number of lines in my log, after which, the initial lines will be discarded (essentially a circular buffer). 我的愿望是实现某种类型的缓冲功能,从而可以在日志中指定最大行数,此后,初始行将被丢弃(本质上是循环缓冲区)。 In this way, I am not wasting a bunch of RAM and slowing down my application by continuing to accumulate logged data, when maybe only the last 20 seconds of data is important. 这样,当可能只有最后20秒的数据很重要时,我不会浪费大量的RAM并通过继续累积记录的数据来减慢我的应用程序的速度。 I attempted this by modifying the logLine() method as follows: 我尝试通过如下修改logLine()方法来尝试此操作:

void ControlDemo::logLine(std::vector<double> data) {
  static int line_num = 0;
  if(line_num < m_max_num_lines)
    line_num++;
  else  // discard first line before inserting next line
    m_logged_data.ignore(1000,'\n');  // move get ptr to next line

  m_logged_data << getCurrentTime();
  for (std::vector<double>::iterator it = data.begin(); it != data.end(); ++it)
    m_logged_data << ', ' << *it;
  m_logged_data << std::endl;
}

However the m_logged_data.str() that is called in writeLogFile() still returns all logged data rather than just the last m_max_num_lines , which suggests that moving the stringstream get pointer with ignore() does not affect the size of the buffer used by the stringstream object or optimize the code in any of the ways that I intended. 但是,在m_logged_data.str()中调用的writeLogFile()仍返回所有记录的数据,而不是仅返回最后一个m_max_num_lines ,这表明使用ignore()移动stringstream get指针不会影响stringstream使用的缓冲区的大小。以我想要的任何方式来反对或优化代码。

I feel like either I am missing something obvious about the stringstream class (why isn't there a setBufferSize() ?? see this ), or I should be using something else to manage the logging functionality. 我感觉或者我缺少关于stringstream类的明显信息(为什么没有setBufferSize() ?参见this ),或者我应该使用其他方法来管理日志记录功能。 If the latter, any suggestions on what class I should be using? 如果是后者,请问我应该使用哪种课程? One of the nice things about stringstream is all of the built-in formatting features, which I am using (although I didn't show it in the code snippets above). 关于stringstream的一件好事是所有我正在使用的内置格式化功能(尽管我没有在上面的代码片段中显示它)。 So, retaining those formatting capabilities would be nice if possible. 因此,如果可能的话,保留这些格式化功能将是不错的选择。

If you really like to use std::stringstream, you can create an array of std::stringstream and use it as your own ring buffer. 如果您真的想使用std :: stringstream,则可以创建一个std :: stringstream数组并将其用作自己的环形缓冲区。 Here is a very small example: 这是一个非常小的示例:

#include <iostream>
#include <fstream>
#include <sstream>

class CyclicStreamer {
public:
    CyclicStreamer(int _n, std::string _filename) : n(_n), cur(0), filename(_filename){
        s = new std::stringstream [n];
    };
    ~CyclicStreamer() {
        delete s;
    };
    void LogLine(int data) {
        s[cur].str(std::string()); // clear the stringstream
        s[cur] << data << std::endl;
        cur =  (cur+1) % n;
    }
    void LogToFile(){
        std::ofstream file;
        file.open(filename.c_str());
        for(int i=cur;i<n;i++){
            file << s[i].str();
        }
        for(int i=0;i<cur;i++){
            file << s[i].str();
        }
        file.close();
    }
private:
    int n;
    int cur;
    std::string filename;
    std::stringstream *s;
};

int main() {
    CyclicStreamer cs = CyclicStreamer(10, "log.txt");
    for(int i=0; i<=20; i++){
        cs.LogLine(i);
    }
    cs.LogToFile();
    return 0;
}

Do note that if you use the example here in your own project with a pretty large m_max_num_lines , you will get into trouble because you might end up using a lot more memories than expected because for each std::stringstream it takes a bit of memory itself. 请注意,如果您在自己的项目中使用此处的示例,且m_max_num_lines很大,则会遇到麻烦,因为最终可能会使用比预期多得多的内存,因为对于每个std :: stringstream,它本身都会占用一些内存。 A better way of doing it is to allow each of them to store part of m_max_num_lines . 更好的方法是允许它们每个存储m_max_num_lines一部分。 Say, if m_max_num_lines is 1000, then you can have only 10 std::stringstream, each storing a maximum of 100 lines of log. 说,如果m_max_num_lines为1000,则您只能有10个std :: stringstream,每个存储最多100行日志。 When a std::stringstram has already stored 100 lines of log, cur = (cur+1) % n . 当std :: stringstram已存储100行日志时, cur = (cur+1) % n

Do note that it would be much better if you can just use a char array and manage it by yourself. 请注意,如果只使用char数组并自己管理它,那就更好了。 You save both memory and time, but as you said, you lose those built-in formatting features, although there're also very nice features of formatting a raw char array, eg sprintf() . 您可以节省内存和时间,但是正如您所说的,您会丢失那些内置的格式化功能,尽管格式化原始char数组也有很好的功能,例如sprintf()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM