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.