 Jo So - 4 years ago 98
C Question

# Faster than scanf?

I was doing massive parsing of positive integers using

`scanf("%d", &someint)`
. As I wanted to see if scanf was a bottleneck, I implemented a naive integer parsing function using
`fread`
, just like:

``````int result;
char c;

while (fread(&c, sizeof c, 1, stdin), c == ' ' || c == '\n')
;

result = c - '0';
while (fread(&c, sizeof c, 1, stdin), c >= '0' || c <= '9') {
result *= 10;
result += c - '0';
}

return result;
``````

But to my astonishment, the performance of this function is (even with inlining) about 50% worse. Shouldn't there be a possibility to improve on scanf for specialized cases? Isn't
`fread`
supposed to be fast (additional hint: Integers are (edit: mostly) 1 or 2 digits)? Jo So
Answer Source

The overhead I was encountering was not the parsing itself but the many calls to `fread` (same with `fgetc`, and friends). For each call, the libc has to lock the input stream to make sure two threads aren't stepping on each other's feet. Locking is a very costly operation.

What we're looking for is a function that gives us buffered input (reimplementing buffering is just too much effort) but avoids the huge locking overhead of `fgetc`.

If we can guarantee that there is only a single thread using the input stream, we can use the functions from `unlocked_stdio(3)`, such as `getchar_unlocked(3)`. Here is an example:

``````static int parseint(void)
{
int c, n;

n = getchar_unlocked() - '0';
while (isdigit((c = getchar_unlocked())))
n = 10*n + c-'0';

return n;
}
``````

The above version doesn't check for errors. But it's guaranteed to terminate. If error handling is needed it might be enough to check `feof(stdin)` and `ferror(stdin)` at the end, or let the caller do it.

My original purpose was submitting solutions to programming problems at SPOJ, where the input is only whitespace and digits. So there is still room for improvement, namely the `isdigit` check.

``````static int parseint(void)
{
int c, n;

n = getchar_unlocked() - '0';
while ((c = getchar_unlocked()) >= '0')
n = 10*n + c-'0';

return n;
}
``````

It's very, very hard to beat this parsing routine, both performance-wise and in terms of convenience and maintainability.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download