2

I have to send the same string (e.g. a log message) to multiple streams.
Which of the following solutions is the most efficient?

  1. Rebuild the same string for each stream and send it to the stream itself.

    outstr1 << "abc" << 123 << 1.23 << "def" << endl;  
    outstr2 << "abc" << 123 << 1.23 << "def" << endl;  
    outstr3 << "abc" << 123 << 1.23 << "def" << endl;  
    
  2. Build the string once with string's operators, and send it to all the streams.

    std::string str = "abc" + std::to_string(123) + std::to_string(1.23) + "def";  
    outstr1 << str;  
    outstr2 << str;  
    outstr3 << str;  
    
  3. Build the string once with a stream, and send it to all the streams:

    std::stringstream sstm;  
    sstm << "abc" << 123 << 1.23 << "def" << endl;  
    std::string str = sstm.str();  
    outstr1 << str;  
    outstr2 << str;  
    outstr3 << str;  
    

Some or all of these output streams could be on a RAM disk.

Any other ways to do the same thing?

Kerrek SB
  • 428,875
  • 83
  • 813
  • 1,025
Pietro
  • 10,628
  • 22
  • 80
  • 165

3 Answers3

5

I would use a tee output stream. Do something like (pseudocode):

allstreams = tee(outstr1, outstr2, outstr3);
allstreams << "abc" << 123 << 1.23 << "def" << endl;

There doesn't seem to be anything in the standard c++ library to do this, but Boost has one.

See also the answers to How can I compose output streams, so output goes multiple places at once?

Community
  • 1
  • 1
Keith Randall
  • 22,422
  • 1
  • 32
  • 53
3

Although it is unlikely that you would see much difference either way1, option #3 sounds the most plausible: unlike the first option, it does not convert ints to strings multiple times; unlike the second option, it does not allocate and delete multiple string objects for its intermediate results2. It also looks cleanest from the readability point of view: no code is duplicated, and output looks like an output, not like a concatenation.


1 Insert a mandatory disclaimer about optimization before profiling being evil here.

2 Small String Optimization may help on systems where it is supported (thanks, Prætorian), but the constructor and destructor calls for the intermediate objects are not going away.

Community
  • 1
  • 1
Sergey Kalinichenko
  • 675,664
  • 71
  • 998
  • 1,399
2

The "proper" way to do something like this is to have a stream buffer writing to multiple destinations and use this stream buffer via a std::ostream. This way the code looks as if it writing just once but the characters are sent multiple times. Searching for "teebuf Dietmar" will find a few variations on the same theme.

To also comment on your question: Which one of the three alternatives is the fastest depends on the exact expressions you have:

  1. needs to evaluate the involved expressions and perform the conversions three times. Depending on what you actually do this may still be fairly fast.
  2. actually creates and destroys multiple streams and does multiple allocations for std::string. I'd expect this to be slowest.
  3. still creates a stream (which should actually be a std::ostringstream) and allocates some memory. Out of your options I'd expect it to be fastest.

Using a teebuf is probably fastest, at least, when it does some buffering but uses only fixed suze arrays, both for the buffer and the array of stream buffer pointers. Note, that you'll need to override sync() to deal with the buffer in a timely manner, though.

To determine the actual performance you'll need to measure!

Dietmar Kühl
  • 141,209
  • 12
  • 196
  • 356