3

I have always thought that calling putc multiple times is faster than puts or printf. To print "hello" for example, in a real program I would always use puts or printf, but now I'm writing a program that generates C code, so I was wondering whether to generate code as putchar('h'); putchar('e') ... because I first thought it should be more faster. But I ran a test which gave a very interesting result. The compiler is GCC.

#include <stdio.h>
#include <time.h>

int main() {
    time_t timer;
    FILE *f;
    int i;

    f = fopen("out.txt", "w");

    #define START_TIMER(TIMER) TIMER = clock()
    #define ELAPSED_TIME(TIMER)\
    (double)(clock() - TIMER) / (double)CLOCKS_PER_SEC

    enum { NUM_ITERS = 9999999 };
    START_TIMER(timer);
    for (i = 0; i < NUM_ITERS; i++) {
        putc('h', f);
        putc('e', f);
        putc('l', f);
        putc('l', f);
        putc('o', f);
        putc('\n', f);
    }
    printf("%.3f\n", ELAPSED_TIME(timer));
    START_TIMER(timer);
    for (i = 0; i < NUM_ITERS; i++) {
        fputs("hello", f);
    }
    printf("%.3f\n", ELAPSED_TIME(timer));
    START_TIMER(timer);
    for (i = 0; i < NUM_ITERS; i++) {
        fprintf(f, "hello\n");
    }
    printf("%.3f\n", ELAPSED_TIME(timer));
    return 0;
}

result without optimization:

4.247 1.013 1.195

result with optimization (-O2):

0.910 1.184 1.315

result with optimization (-O3):

0.920 1.158 1.311

So calling putc multiple times is slower than puts of printf when executing naively without optimization. First, I'm curious why so. Second, which way should I follow for the program-generated C code?

jww
  • 97,681
  • 90
  • 411
  • 885
  • 2
    Have you tried a longer string than just "hello"? – R.. GitHub STOP HELPING ICE Oct 06 '14 at 22:30
  • 1
    a) Is this the most important problem you could be working on for this program right now? b) Anyone who has to maintain a `putc()` version will hunt you down and poke you in the eye with a pointy stick. – Mike Sherrill 'Cat Recall' Oct 06 '14 at 22:30
  • @Mike The machine-generated code is not intended for human reading. –  Oct 06 '14 at 22:31
  • @xiver77: When things don't work right, someone will read the code. – Mike Sherrill 'Cat Recall' Oct 06 '14 at 22:33
  • @R.. I've just tried with "hellohellohellohellohellohellohellohellohellohello" with -O0, the result is `35.488 5.232 5.844` and with -O3 it is `6.906 5.051 5.867` so calling putc multiple times seems to be even more slower with longer text, even with optimization. –  Oct 06 '14 at 22:38
  • 1
    If the machine generated code is not intended for human reading then why does it matter how it looks? Just generate whatever works and is correct. – Brandin Oct 06 '14 at 22:38
  • Btw fprintf has to do formatting of the string you give it so it makes sense that it takes some extra overhead. – Brandin Oct 06 '14 at 22:40
  • @Brandin What I truly need now is a `puts` without an additional '\n' at the end. –  Oct 06 '14 at 22:41
  • 3
    @xiver77 what's wrong with `fputs("foo",fp)` – Brandin Oct 06 '14 at 22:43
  • @Brandin thanks I've just re-checked the reference. –  Oct 06 '14 at 22:44

1 Answers1

6

Your intuition for what should be faster is wrong. Generally, putc/putchar is going to have a lot of overhead per byte written, since there's a whole cycle of function call, potential locking of the stdio stream (stdout) being targetted, etc. per byte. On the other hand, functions like printf or puts have more overhead per call than putc (e.g. printf has to process the format string, and puts has to call strlen or equivalent), but that overhead only happens once, no matter how many bytes you're writing. The actual write can take place as a bulk copy to the FILE's buffer, or a bulk write to the underlying file (if the FILE is unbuffered).

As for how optimization levels affect this, -O0 probably has a lot more overhead around making the function call which gets optimized out at higher optimization levels.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711