Reputation: 279
Let's say I have one line of printf()
with a long string:
printf( "line 1\n"
"line 2\n"
"line 3\n"
"line 4\n"
"line 5\n"
"line 6\n"
"line 7\n"
"line 8\n"
"line 9\n.. etc");
What are the costs incurred by this style compared to having multiple printf()
's for each line?
Would there be a possible stack overflow if the string is too long?
Upvotes: 21
Views: 6461
Reputation: 106122
what are the costs incurred by this style compared to having multiple printf()'s for each line ?
Multiple printf
will result in multiple function calls and that's the only overhead.
Would there be a possible stack overflow if the string is too long?
No stack overflow in this case. String literals are generally stored in read only memory, not in stack memory. When a string is passed to printf
then only a pointer to its first element is copied to the stack.
Compiler will treat this multi line string
"line 1\n"
"line 2\n"
"line 3\n"
"line 4\n"
"line 5\n"
"line 6\n"
"line 7\n"
"line 8\n"
"line 9\n.. etc"
as single string
"line 1\nline 2\nline 3\nline 4\nline 5\nline 6\nline 7\nline 8\nline 9\n.. etc"
and this will be stored in read only section of the memory.
But note that (pointed by pmg in a comment) C11 standard section 5.2.4.1 Translation limits says that
The implementation shall be able to translate and execute at least one program that contains at least one instance of every one of the following limits18):
[...]
Upvotes: 17
Reputation: 179717
printf
is a slow function if you are only outputting constant strings, because printf
has to scan each and every character for a format specifier (%
). Functions like puts
are significantly faster for long strings because they can basically just memcpy
the input string into the output I/O buffer.
Many modern compilers (GCC, Clang, probably others) have an optimization that automatically converts printf
into puts
if the input string is a constant string with no format specifiers that ends with a newline. So, for example, compiling the following code:
printf("line 1\n");
printf("line 2\n");
printf("line 3"); /* no newline */
results in the following assembly (Clang 703.0.31, cc test.c -O2 -S
):
...
leaq L_str(%rip), %rdi
callq _puts
leaq L_str.3(%rip), %rdi
callq _puts
leaq L_.str.2(%rip), %rdi
xorl %eax, %eax
callq _printf
...
in other words, puts("line 1"); puts("line 2"); printf("line 3");
.
If your long printf
string does not end with a newline, then your performance could be significantly worse than if you made a bunch of printf
calls with newline-terminated strings, simply because of this optimization. To demonstrate, consider the following program:
#include <stdio.h>
#define S "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
#define L S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S
/* L is a constant string of 4000 'a's */
int main() {
int i;
for(i=0; i<1000000; i++) {
#ifdef SPLIT
printf(L "\n");
printf(S);
#else
printf(L "\n" S);
#endif
}
}
If SPLIT
is not defined (producing a single printf
with no terminating newline), the timing looks like this:
[08/11 11:47:23] /tmp$ cc test.c -O2 -o test
[08/11 11:47:28] /tmp$ time ./test > /dev/null
real 0m2.203s
user 0m2.151s
sys 0m0.033s
If SPLIT
is defined (producing two printf
s, one with a terminating newline, the other without), the timing looks like this:
[08/11 11:48:05] /tmp$ time ./test > /dev/null
real 0m0.470s
user 0m0.435s
sys 0m0.026s
So you can see, in this case splitting the printf
into two parts actually produces a 4x speedup. Of course, this is an extreme case, but it illustrates how printf
may be variably optimized depending on the input. (Note that using fwrite
is even faster - 0.197s - so you should consider using that if you really want speed!).
tl;dr: if you are printing only large, constant strings, avoid printf
entirely and use a faster function like puts
or fwrite
.
Upvotes: 10
Reputation: 7812
Each additional printf (or puts if your compiler optimizes it that way) will incur the system specific function call overhead each time, though there's a good probability that optimization will combine them anyhow.
I have yet to see a printf implementation that was a leaf function, so expect additional function call overheads for something like vfprintf and it's callees.
Then you'll likely have some sort of system call overheads for each write. Since printf uses stdout, which is buffered, some of these (really costly) context switches could normally be avoided... except all of the examples above end with new lines. Most of your cost will probably be here.
If you are really worried about cost in your main thread, move this kind of stuff to a separate thread.
Upvotes: 1
Reputation: 2330
A printf
without format modifiers is silently replaced (aka. optimized) to a puts
call. This is already a speedup. You don't really want to lose that on calling printf
/puts
multiple times.
GCC has printf
(among others) as a builtin, so it can optimize the calls during compile time.
See:
Upvotes: 5
Reputation: 21965
C concatenates string literals if they are separated by nothing or by whitespace. So below
printf( "line 1\n"
"line 2\n"
"line 3\n"
"line 4\n"
"line 5\n"
"line 6\n"
"line 7\n"
"line 8\n"
"line 9\n.. etc");
is perfectly fine and stands out in the readability point of view. Also a single printf
call unarguably has lesser overhead than 9 printf
calls.
Upvotes: 11