Сверху я столкнулся не с самим анализом, а с многочисленными вызовами fread
(то же самое с fgetc
и друзьями). Для каждого вызова libc должен заблокировать входной поток, чтобы убедиться, что два потока не наступают друг другу на ноги. Блокировка - очень дорогая операция.
Нам нужна функция, которая дает нам буферизованный ввод (переопределение буферизации - слишком много усилий), но позволяет избежать огромных накладных расходов на блокировку fgetc
.
Если мы можем гарантировать, что существует только один поток, использующий входной поток, мы можем использовать функции из unlocked_stdio(3)
, например getchar_unlocked(3)
. Вот пример:
static int parseint(void)
{
int c, n;
n = getchar_unlocked() - '0';
while (isdigit((c = getchar_unlocked())))
n = 10*n + c-'0';
return n;
}
Данная версия не проверяет наличие ошибок. Но это гарантировано прекратится. Если требуется обработка ошибок, может быть достаточно проверить feof(stdin)
и ferror(stdin)
в конце или позволить вызывающей стороне сделать это.
Моей первоначальной целью было представить решение проблем программирования в SPOJ, где вводятся только пробелы и цифры. Таким образом, есть еще возможности для улучшения, а именно проверка isdigit
.
static int parseint(void)
{
int c, n;
n = getchar_unlocked() - '0';
while ((c = getchar_unlocked()) >= '0')
n = 10*n + c-'0';
return n;
}
Очень и очень трудно побороть эту процедуру синтаксического анализа, как с точки зрения производительности, так и с точки зрения удобства и удобства обслуживания.