14

I am coding for various programming olympiads and am trying to improve time efficiency. I'm looking for the fastest way to get input, using the gcc compiler without any external library.

I've previously used cin and cout, but found that scanf and printf are much faster. Is there an even faster way? I don't care of space complexity that much, I rather prefer better time.

Ted Klein Bergman
  • 9,146
  • 4
  • 29
  • 50
Maggi Iggam
  • 682
  • 1
  • 8
  • 20
  • 2
    Are you looking for implementation time, or run time? For runtime you might be interested in my answer here: http://stackoverflow.com/a/8854366/365496 – bames53 Mar 17 '12 at 05:08
  • Aren't files common in competitions? Fibre Channel SAN drives make them *really* fast :) – Hans Passant Mar 17 '12 at 05:10

3 Answers3

23

That streams are always slower than the C-API functions is a pretty common misconception because by default, they synchronize with the C-layer. So yeah, that's a feature, not a bug.

Without sacrificing type safety (and readability, depending on your taste), you possibly gain performance with streams by using:

std::ios_base::sync_with_stdio (false);

A little indicator:

#include <cstdio>
#include <iostream>

template <typename Test> 
void test (Test t)
{
    const clock_t begin = clock();
    t();
    const clock_t end = clock();
    std::cout << (end-begin)/double(CLOCKS_PER_SEC) << " sec\n";
}

void std_io() {
    std::string line;
    unsigned dependency_var = 0;
    
    while (!feof (stdin)) {
        int c;
        line.clear();
        while (EOF != (c = fgetc(stdin)) && c!='\n')
            line.push_back (c);
        dependency_var += line.size();
    }
    
    std::cout << dependency_var << '\n';
}

void synced() {
    std::ios_base::sync_with_stdio (true);
    std::string line;
    unsigned dependency_var = 0;
    while (getline (std::cin, line)) {
        dependency_var += line.size();
    }
    std::cout << dependency_var << '\n';
}

void unsynced() {
    std::ios_base::sync_with_stdio (false);
    std::string line;
    unsigned dependency_var = 0;
    while (getline (std::cin, line)) {
        dependency_var += line.size();
    }
    std::cout << dependency_var << '\n';
}

void usage() { std::cout << "one of (synced|unsynced|stdio), pls\n"; }

int main (int argc, char *argv[]) {
    if (argc < 2) { usage(); return 1; }
    
    if (std::string(argv[1]) == "synced") test (synced);
    else if (std::string(argv[1]) == "unsynced") test (unsynced);
    else if (std::string(argv[1]) == "stdio") test (std_io);
    else { usage(); return 1; }

    return 0;
}

With g++ -O3, and a big text file:

cat testfile | ./a.out stdio
...
0.34 sec

cat testfile | ./a.out synced
...
1.31 sec

cat testfile | ./a.out unsynced
...
0.08 sec

How this applies to your case depends. Modify this toy-benchmark, add more tests, and compare e.g. something like std::cin >> a >> b >> c with scanf ("%d %d %d", &a, &b, &c);. I guarantee, with optimizations (i.e. without being in debug mode), performance differences will be subtle.

If that does not saturate your needs, you might try other approaches, e.g. reading the whole file first (may or may not bring more performance) or memory maps (which is a non-portable solution, but the big desktops have them).


Update

Formatted input: scanf vs. streams

#include <cstdio>
#include <iostream>

template <typename Test> 
void test (Test t)
{
    const clock_t begin = clock();
    t();
    const clock_t end = clock();
    std::cout << (end-begin)/double(CLOCKS_PER_SEC) << " sec\n";
}

void scanf_() {
    char x,y,c;
    unsigned dependency_var = 0;
    
    while (!feof (stdin)) {
        scanf ("%c%c%c", &x, &y, &c);
        dependency_var += x + y + c;
    }
    
    std::cout << dependency_var << '\n';
}

void unsynced() {
    std::ios_base::sync_with_stdio (false);
    char x,y,c;
    unsigned dependency_var = 0;
    while (std::cin) {
        std::cin >> x >> y >> c;
        dependency_var += x + y + c; 
    }
    std::cout << dependency_var << '\n';
}

void usage() { std::cout << "one of (scanf|unsynced), pls\n"; }

int main (int argc, char *argv[]) {
    if (argc < 2) { usage(); return 1; }
    
    if (std::string(argv[1]) == "scanf") test (scanf_);
    else if (std::string(argv[1]) == "unsynced") test (unsynced);
    else { usage(); return 1; }

    return 0;
}

Results:

scanf: 0.63 sec
unsynced stream: 0.41 
Community
  • 1
  • 1
Sebastian Mach
  • 38,570
  • 8
  • 95
  • 130
  • 3
    The stream are slower than C-API but there is a reason (it does a lot more work). It has the whole Locale stuff built into the readers/writers that is just not there in the C-API and this can be expensive (though the try and minimize it with the "C" locale (but it is still there)). Here we comments assume we are using formatted input/output not the direct read/write. – Martin York Mar 17 '12 at 06:24
  • @LokiAstari: Not necessarily. scanf always requires the format string to be parsed, whereas for streams it is not there at runtime. – Sebastian Mach Mar 17 '12 at 06:36
  • @LokiAstari: Example for formatted input provided. After ignoring it on the first read (sorry): C-io also uses locales (http://linux.die.net/man/3/setlocale). – Sebastian Mach Mar 17 '12 at 06:46
  • @SebastianMach Sir , i need to talk to you , can we chat ? – Suraj Jain Feb 03 '17 at 20:16
5

Generally, buffered input will be the fastest. The less frequently you have to flush your input buffer, the faster the input will be. For a full and very informative discussion, see this question. In short, read() with large buffer sizes is as fast as you can get since it's almost directly on top of the corresponding system call in your OS.

Community
  • 1
  • 1
AerandiR
  • 5,260
  • 2
  • 20
  • 22
0

Probably scanf is somewhat faster than using streams. Although streams provide a lot of type safety, and do not have to parse format strings at runtime, it usually has an advantage of not requiring excessive memory allocations (this depends on your compiler and runtime). That said, unless performance is your only end goal and you are in the critical path then you should really favour the safer (slower) methods.

There is a very delicious article written here by Herb Sutter

http://www.gotw.ca/publications/mill19.htm

who goes into a lot of detail of the performance of string formatters like sscanf and lexical_cast and what kind of things were making them run slowly or quickly. This is kind of analogous, probably to the kind of things that would affect performance between C style IO and C++ style. The main difference with the formatters tended to be the type safety and the number of memory allocations.

shofee
  • 2,079
  • 13
  • 30
  • Note that streams by default sync with the C-API. If you disable syncing, streams will usually be as fast as or sometimes faster than the C-API. – Sebastian Mach Mar 17 '12 at 05:59