1

I have some code compiled under the c++11 standard. I have multiple threads writing to cout. I noticed when writing many lines there would be some cases where some lines were missing (like 1 out of 2000000). I was surprised to see this since my string (outStr below) was local to each thread and I had a critical section around my writes to stdout. I noticed the problem went away when I flushed the stream.

#pragma omp critical(cout)
{
    cout << outStr;
    cout.flush();
}

Is this expected behaviour? What really tricked me was the fact that when I wrote a relatively small number of lines (<100000), I would always see the number of expected lines outputted.

Overall I'm not really happy with the critical section in general since I'm noticing in my profiling that it is causing a lot of contention. I'm open to any suggestions to improve my I/O.

*Edit I was under the impression that under c++11 there would be no corruption of my output so long as I synchronized my output (i.e. no interleaving or missing output when I use a critical section) but the missing lines seem to indicate that this is not a guarantee without also flushing the output.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
cjustin
  • 115
  • 7
  • I don't know what's the problem with disappearing lines, but I would write to separate files and combine them in the end of the run. – n. m. could be an AI Jul 31 '18 at 05:01
  • @n.m. If this is part of pipeline it is nice to be able to write stdout without first writing to disk. – cjustin Jul 31 '18 at 05:02
  • @john the other question doesn't mention that a flush is needed only that the output is synchronized. It is similar but doesn't address why some strings are lost in my output if `cout` in c++11 is thread safe. – cjustin Jul 31 '18 at 05:14
  • I agree that if we look only at the "title" of the question, the other may be a duplicate, but effectively the two questions are really quite different. I've edited this title to more accurately reflect what I believe is the real intent of this question. – Jerry Coffin Jul 31 '18 at 05:16
  • Another method would be using a single dedicated writer and some lock-free queue for passing messages from workers to the writer. – n. m. could be an AI Jul 31 '18 at 05:22
  • @n.m Any lock-free concurrent queues that you would recommend for strings? Thanks. – cjustin Jul 31 '18 at 05:24
  • boost or tbb probably, no real experience with them though so measure. Also, lock-free queues isn't necessarily always faster than ones with locks so measure that too. – n. m. could be an AI Jul 31 '18 at 05:58

2 Answers2

4

Much of the problem here stems from the fact that flushing a stream is quite slow. So, when one thread enters the critical section, they stay in it for quite a while. By the time they finish, there are probably several other threads waiting, so it becomes a severe bottleneck.

Probably the most obvious way to prevent the problem is to have the stream itself owned by one thread, and have a thread-safe queue of things that need to be written to the stream. If you're all right with accumulating a "chunk" of data to go the stream into a string (or some other pre-decided data structure), it's even pretty trivial to do.

I'd note for the record that while you might ultimately want to use a lock-free queue, a fairly simple, old-fashioned queue based on locking will still almost certainly provide a huge improvement over what you're doing right now--inserting a string into a queue is drastically faster than flushing a stream (in the range of nanoseconds to possibly a few microseconds, where flushing is typically on the order of milliseconds).

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • Out of curiosity, do you know why the flush is needed to preserve the output? It is such a nasty thing considering how the output looks almost correct in small test cases. – cjustin Jul 31 '18 at 05:36
  • No. Lack of flushing allows output from different threads to interleave arbitrarily, but that *should* be about all. I suspect you're seeing a bug in the standard library implementation you're using, but we'd need to examine the code and the output with considerable care to be certain whether that's the case or not. If you want, you *could* try the synchronized stream and transaction classes I posted in [another answer](https://stackoverflow.com/a/49157022/179910). I'm not certain it'll be helpful, but it might. – Jerry Coffin Jul 31 '18 at 05:39
  • Very odd. I'm using gcc version 5.5.0. I've confirmed I am indeed missing characters that should have been outputted without the flush. I should add that I do flush at the very end of the program as well. – cjustin Jul 31 '18 at 05:53
  • If so, that sure sounds to me like it just about has to qualify as a bug. – Jerry Coffin Jul 31 '18 at 05:56
  • The issues seemed to go away after I removed `std::cout.sync_with_stdio(false); ` from by code which is strange since I don't use any printf in my code – cjustin Jul 31 '18 at 21:36
0

It seems std::cout.sync_with_stdio(false); was the culprit. I hadn't expected this to be the issue but I noticed it after I made a simple test program. I had used the function in an attempt to speed up my I/O which turned out to be a bad idea.

It seems, although in a critical section, the I/O stream was not thread-safe. I was also not using any printfs in my code.

According to the documentation:

In addition, synchronized C++ streams are guaranteed to be thread-safe (individual characters output from multiple threads may interleave, but no data races occur)

It would seem that the without syncing the buffer would decide to flush at random intervals for some reason.

After removed this I was actually able to get coherent output with a simple `cout << outStr; statement without flushing. Also, I am still doing testing and its seems that I may not even need the critical section at all.

cjustin
  • 115
  • 7