0

When writing single characters to an output stream, the purist in me wants to use single quotes (e.g.):

unsigned int age{40};
std::ostringstream oss;
oss << "In 2022, I am "      << age      << '\n';  // 1. Single quotes around \n
oss << "In 2023, I will be " << age + 1u << "\n";  // 2. Minor ick--double quotes around \n

Because I'm writing a single character and not an arbitrary-length message, it doesn't seem necessary to have to provide a null-terminated string literal.

So I decided to measure the difference in speed. Naively, I'd expect option 1, the single-character version, to be faster (only one char, no need to handle \0). However, my test with Clang 13 on quick-bench indicates that option 2 is a hair faster. Is there an obvious reason for this?

https://quick-bench.com/q/3Zcp62Yfw_LMbh608cwHeCc0Nd4

Of course, if the program is spending a lot of time writing data to a stream anyway, chances are the program needs to be rethought. But I'd like to have a reasonably correct mental model, and because the opposite happened wrt what I expected, my model needs to be revised.

KyleKnoepfel
  • 1,426
  • 8
  • 24
  • That's likely going to strongly dependent on what code exactly you tested, with what compiler and options exactly and which standard library. There is no obvious reason that either should be faster than the other. – user17732522 Jul 26 '22 at 16:08
  • 2
    For this specific case, it's somewhat related to my own old question ["\n" or '\n' or std::endl to std::cout?](https://stackoverflow.com/questions/8311058/n-or-n-or-stdendl-to-stdcout) and [its voted duplicate](https://stackoverflow.com/questions/213907/stdendl-vs-n). – Some programmer dude Jul 26 '22 at 16:08
  • More generically: If you only want to write a single character, write a single character. A string needs pointers and indirection as well as loops. – Some programmer dude Jul 26 '22 at 16:10
  • 1
    Writing performance test (in quick bench or other tool) is harder then you think. Optimizer can do such things that your measurement may be invalid. So please provide code which does measurement! Also included code is more different then just quotes vs apostrophes. – Marek R Jul 26 '22 at 16:15
  • You can copy paste your test to this online site: https://quick-bench.com/ – Marek R Jul 26 '22 at 16:18
  • Added link above to quick-bench test with Clang 13 (c++20/libc++) – KyleKnoepfel Jul 26 '22 at 16:19
  • As you can see test in link says char literal is just a bit faster. – Marek R Jul 26 '22 at 16:20
  • @MarekR, other way around. String literal is 45.5 and char literal 46.6 (lower is better). – KyleKnoepfel Jul 26 '22 at 16:22
  • @MarekR other way around, string seems faster than char according to those results. – Blindy Jul 26 '22 at 16:23
  • 2
    You can go ahead and use `'\n'`. On my platform, the `operator<<` for that will make a 2 character array, set the second character to `'\0'` and set the first to `'\n'`, and then call it as if you had done `<< "\n"` in the first place. – Eljay Jul 26 '22 at 16:31
  • 1
    I've analyzed assembly for this code and compiler optimized this to the point that differences are cosmetic (string literal is unwrapped to character). So difference you are seeing is just "luck" - statistical noise and results are equivalent. See here https://quick-bench.com/q/kdlII-GuitAGsXuj0Q-H8GYigg4 – Marek R Jul 26 '22 at 16:36

1 Answers1

3

As you can see in the assembly and in the libc++ source here, both << operations in the end call the same function __put_character_sequence which the compiler decided to not inline in either case.

So, in the end you are passing a pointer to the single char object anyway and if there is a pointer indirection overhead it applies equally to both cases.

__put_character_sequence also takes the length of the string as argument, which the compiler can easily evaluate at compile-time for "\n" as well. So there is no benefit there any way either.

In the end it probably comes down to the compiler having to store the single character on the stack since without inlining it can't tell whether __put_character_sequence will modify it. (The string literal cannot be modified by the function and also would have the same identity between iterations of the loop.)

If the standard library used a different approach or the compiler did inline slightly differently, the result could easily be the other way around.

user17732522
  • 53,019
  • 2
  • 56
  • 105