Reputation: 165
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
Reputation: 23510
Not sure if my benchmarks are correct at all.. It is my first time testing speed really..
Anyway, here goes:
#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
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