Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Speed up std::cout logging with stringstreams

Tags:

c++

I'm trying to speed up a multithreaded program that prints a lot to std::cout. Most of the stuff that is printed are strings puzzled together from several variables (strings, numbers etc). Access to std::cout is protected by a mutex to prevent printouts from several threads from getting intermingled in the manner of:

{
    std::lock_guard<std::mutex> lock(mutex);
    std::cout << stringA << " 1 " << 5  << 'C' << std::endl;
}

Measurements showed that several threads spend a lot of time waiting for the mutex as std::cout seems to take some time for large and complex strings.

My question now is:

Can I in theory reduce lock contention by assembling the string into a std::stringstream before entering the mutex, then send the already assembled string to std::cout? As in:

{
    std::stringstream ss;
    ss << stringA << " 1 " << 5  << 'C' << std::endl;
    std::lock_guard<std::mutex> lock(mutex);
    std::cout << ss.str();
}

If yes, can this be improved further?

like image 504
Desperado17 Avatar asked Apr 16 '26 02:04

Desperado17


2 Answers

Can I in theory reduce lock contention by assembling the string into a std::stringstream before entering the mutex, then send the already assembled string to std::cout?

Absolutely. operator<< has to do some work to format the types passed in. Assembling the string into a std::stringstream means that you do all that work up front and just have to write out the assembled string to std::cout, meaning you spend less time under the lock.

However, note that ss.str() returns a std::string by value. This means that you are copying the string inside the critical region. It would be better to write std::cout << ss.rdbuf() and write the underlying string inside the std::stringstream directly.

Beyond that, you'll want to reduce the time spent outputting to std::cout as much as possible. If you never call any C stdio functions, you should probably call std::ios_base::sync_with_stdio(false)

Bringing this together:

// Near the beginning of your program:
std::ios_base::sync_with_stdio(false);

// ...

{
    // Prefer using ostringstream if you never need to read from it
    std::ostringstream ss;
    // std::endl is never needed. Use '\n' instead. If you want to flush,
    // explicitly write `ss << '\n' << std::flush`. For stringstreams, I believe
    // it doesn't matter, but it's good to get into the habit of doing this
    ss << stringA << " 1 " << 5  << 'C' << '\n';
    std::lock_guard<std::mutex> lock(mutex);
    std::cout << ss.rdbuf();
}
like image 163
Justin Avatar answered Apr 17 '26 16:04

Justin


I would drop string streams altogether in favor of std::string::append and std::to_string. streams tend to drag a lot of locale-oriented stuff and make the implementation heavier than the raw string operations. I would go with this:

 std::string str;
 str.append(stringA).append(" 1 ").append('C').append('\n');
 std::cout << str;
like image 32
David Haim Avatar answered Apr 17 '26 16:04

David Haim



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!