3

Both this answer on Stack Overflow and cppreference.com suggest turning off stream synchronization to improve performance, arguing that stream synchronization would disable buffering.

Now this is what I do not understand. Why can't synchronized streams simply share buffers? I imagined that if the streams are synchronized, std::fputc(stdout, c); could simply be implemented in terms of std::cout << c; or the other way round (or using a common primitive). So whenever C I/O is mixed with C++ I/O, synchronized streams would even have an advantage over non-synchronized ones! Fewer buffers, fewer cache misses.

The current C++ standard draft seems to be with me here. In the footnote where sync_with_stdio() is specified, it says "In practical terms, synchronization usually means that a standard iostream object and a standard stdio object share a buffer." Is it possible that the links I posted above merely document some imperfect implementations and their performance implications?

Also, because I do not see any theoretical disadvantage of non-synchronized streams, I am wondering why these exist in the first place.

purefanatic
  • 933
  • 2
  • 8
  • 23
  • As a side note, cppreference suggests that `sync_with_stdio()` impacts the thread safety of the standard streams. I think that is just plain wrong. – purefanatic Mar 12 '18 at 15:08
  • 1
    `std::streambuf` and whatever buffers underpin `stdio` can't be the same (they have different interfaces). So, from my reading of the link, `std::streambuf` implementation outputs to *stdio* buffer without doing any buffering itself. So they do share a buffer but *iostreams* have one extra layer of indirection to get to it. Also they must have additional thread locking so as to remain thread-safe with *stdio* operations. – Galik Mar 12 '18 at 15:19
  • I don't quite follow. Synchronization or not, both share the same device, which must always be accessed mutually exclusively. So a single mutex to protect both cin and stdin at the same time should suffice, no? – purefanatic Mar 12 '18 at 15:43
  • Well `C++` doesn't have access to the ultimate device, that's the shell's responsibility. But even so, before you get to the device you have the shared buffer which needs a mutex between stdio and iostream. I suppose they could share a mutex for that, but that would couple both libraries together quite strongly and I have no idea what demons lie there. Regardless I think *iostream* probably has to have its own independent locking to fulfill its thread-safe obligations across all *streams*, not just `cin/cout`. So I suspect it has to lock *two* mutexes to sync with stdio. – Galik Mar 12 '18 at 16:06
  • @purefanatic `sync_with_stdio()` impacts the thread safety of the standard streams as specified in http://eel.is/c++draft/iostream.objects#overview-5 – Cubbi Mar 16 '18 at 01:46
  • @Cubbi Thanks for pointing that out! Of course I missed that part of the specification. – purefanatic Mar 17 '18 at 17:43
  • By the way, I also fail to see the design rationale behind this character-level mutual exclusion. When would that actually be desirable? To me, it just seems like a shitty safety measure made for shitty programmers that do not care about thread safety, adding unnecessary runtime cost for _everyone_. – purefanatic May 10 '18 at 17:21
  • Also, is there a way to disable standard stream thread safety from plain C? Or does a C programmer have to pay the additional runtime cost all the time? I feel like standard stream thread safety and synchronization should be two separate settings. Note that these are rhetorical questions. C/C++ standard I/O doesn't suit high-performance needs either way... – purefanatic May 10 '18 at 17:24

2 Answers2

1

std::fputc(stdout, c); could simply be implemented in terms of std::cout << c; or the other way round (or using a common primitive)

It is actually "the other way round". The synchronized std::cout is an unbuffered stream, and each std::cout << c; immediately executes std::fputc(stdout, c);.

synchronized streams would even have an advantage over non-synchronized ones! Fewer buffers, fewer cache misses

It's just one buffer either way: stdout's when synchronized or std::cout's when not. On my gcc/libstdc++, the main difference is that one is 1024 bytes and the other is 8191 (seriously). It might be interesting to profile the three existing implementations of the standard library (libstdc++, libc++, and MSVC) to spot the differences and what causes them. It may very well be that they are "imperfect implementations" - there is no reason unsynchronized std::cout << c; should ever be slower than (always synchronized) std::fputc(stdout, c);.

Cubbi
  • 46,567
  • 13
  • 103
  • 169
  • "each std::cout << c; immediately executes std::fputc(stdout, c);." that seems reasonable, nice. But are you sure about "It's just one buffer either way: stdout's when synchronized or std::cout's when not." Wouldn't C-style I/O still use stdout's buffer in the non-synchronized case, while C++-style I/O would use its own? In this case, two buffers would be coexisting. That's why my point is rather the other way round. I think that there should be no reason for _synchronized_ I/O being slower than _unsynchronized_ I/O! – purefanatic Mar 17 '18 at 18:17
  • yes, C I/O uses stdout's buffer. I meant "for C++ I/O" there's one buffer either way. As for slowness, synchronized incurs the cost of a mutex. That's why C I/O usually comes with nonportable unsynchronized API, such as GNU `putchar_unlocked` – Cubbi Mar 18 '18 at 03:52
  • `8191` or is it `8192`? defined as `BUFSIZ` (note: `8192` on Nix's and `512` on windows) The base macro in gcc source is `_IO_BUFSIZ` – David C. Rankin Mar 25 '18 at 06:06
  • @DavidC.Rankin yes, `stdio_filebuf` says it uses BUFSIZ, but when I run with strace, I am seeing calls to write() with the size of `8191` – Cubbi Mar 25 '18 at 15:01
  • Lord knows where the extra byte goes, there is no question it is defined as `8192` in the source. -- If that's the only byte I lose in a day -- it's been a good day. – David C. Rankin Mar 25 '18 at 17:57
  • I am accepting this answer because Cubbi was first to point out that non-synchronized streams are not thread-safe. Omitting the mutex might indeed improve performance by a tiny margin. Disregarding the thread-safety, I think I am still missing the point of non-synchronized streams, because in my opinion, synchronizing C streams and C++ streams should be entirely free or even beneficial to performance, as I have pointed out before. – purefanatic May 10 '18 at 16:49
0

There is a perfect answer here that I recommend reading. According to that answer originally writen by Ionut, "If you disable the synchronization, then C++ streams are allowed to have their own independent buffers". It is also discussed here in this book, in chapter 11 I think.

geizio
  • 105
  • 1
  • 16