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
.