CPPCoder
CPPCoder

Reputation: 165

C++ : Fastest way to read string from stdin

We can use getchar_unlocked for fast reading of integers from stdin by manipulating the characters like:

int scan_d()
{
    int ip = getchar_unlocked(), ret = 0, flag = 1;

    for(; ip < '0' || ip > '9'; ip = getchar_unlocked())
    {
        if(ip == '-')
        {
            flag = -1;
            ip = getchar_unlocked();
            break;
        }
    }

    for(; ip >= '0'&& ip <= '9'; ip = getchar_unlocked())
        ret = ret * 10 + ip - '0';
    return flag * ret;
}

Is there a way to read strings from stdin in a fast manner using something like above? gets is faster than cin/scanf but at the same time possess critical handling of whitespaces. I thought of modifying the above code for strings but faced problems with whitespaces. Further, it seems reading every character of a string one by one will be slower.

By stdin I mean string is entered by the user (no file handling)

Upvotes: 2

Views: 11380

Answers (2)

Brandon
Brandon

Reputation: 23510

Not sure if my benchmarks are correct at all.. It is my first time testing speed really..

Anyway, here goes:

http://ideone.com/KruGD2

#include <cstdio>
#include<iostream>
#include <sstream>
#include <chrono>

std::chrono::time_point<std::chrono::steady_clock> hClock()
{
    return std::chrono::steady_clock::now();
}

std::uint32_t TimeDuration(std::chrono::time_point<std::chrono::steady_clock> Time)
{
    return std::chrono::duration_cast<std::chrono::nanoseconds>(hClock() - Time).count();
}


void Benchmark(const char* Name, std::string &str, void(*func)(std::string &str))
{
    auto time = hClock();
    for (int i = 0; i < 100; ++i)
    {
        func(str);
        str.clear();
    }
    std::cout<<Name<<" took: "<<TimeDuration(time) / 100<<" nano-seconds.\n";
}

void unlocked_bench(std::string &str)
{
    char c = '0';
    while((c = getchar_unlocked()) && (c != -1 && c != '\n' && c != '\r'))
    {
        str += c;
    }
}

void getchar_bench(std::string &str)
{
    char c = '0';
    while((c = getchar())  && (c != -1 && c != '\n' && c != '\r'))
    {
        str += c;
    }
}

void getline_bench(std::string &str)
{
    std::cin.getline(&str[0], str.size());
}

void scanf_bench(std::string &str)
{
    scanf("%[^\n]100s", &str[0]);
}

void fgets_bench(std::string &str)
{
    fgets(&str[0], str.size(), stdin);
}

void cinread_bench(std::string &str)
{
    std::cin.read(&str[0], str.size());
}

int main()
{
    std::string str;
    str.reserve(100);

    Benchmark("getchar_unlocked", str, unlocked_bench);
    Benchmark("getchar", str, getchar_bench);
    Benchmark("getline", str, getline_bench);
    Benchmark("scanf", str, scanf_bench);
    Benchmark("fgets", str, fgets_bench);
    Benchmark("cinread", str, cinread_bench);

    return 0;
}

Input:

Hello There
Hello There
Hello There
Hello There
Hello There
Hello There

Output:

getchar_unlocked took: 436 nano-seconds.
getchar took: 330 nano-seconds.
getline took: 619 nano-seconds.
scanf took: 522 nano-seconds.
fgets took: 44 nano-seconds.
cinread took: 67 nano-seconds.

Upvotes: 4

Mooing Duck
Mooing Duck

Reputation: 66922

Is there a way to read strings from stdin in a fast manner using something like above?

Certainly. If you were clearer how you expected it to act, we could even provide code.

gets is faster than cin/scanf but at the same time possess critical handling of whitespaces.

cin and scanf can do that as well: How to cin Space in c++? and How do you allow spaces to be entered using scanf?

I thought of modifying the above code for strings but faced problems with whitespaces.

What problems? https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem

Further, it seems reading every character of a string one by one will be slower.

Slower than a block read? Sure. But then, reading one character at a time is really the only way to tell where the end of the string is. You could block read into a buffer and spin through that to find the end of the string, this is called buffering. But stdin is already buffered, so buffering it again will make it slower not faster. There isn't going to be a faster way of reading a space separated string than using getchar_unlocked one character at a time.

Upvotes: 3

Related Questions