比scanf更快?

Jo *_* So 6 c input scanf numeric-input

我正在使用大量解析正整数scanf("%d", &someint).正如我想看看scanf是否是一个瓶颈,我实现了一个天真的整数解析函数fread,就像:

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;
Run Code Online (Sandbox Code Playgroud)

但令我惊讶的是,这个功能的表现(即使内联)也差了不到50%.对于特殊情况,是否应该有可能改进scanf?不fread应该是快速的(附加提示:整数是(编辑:大多数)1或2位数?)?

Jo *_* So 8

我遇到的开销不是解析本身,而是许多调用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;
}
Run Code Online (Sandbox Code Playgroud)

以上版本不检查错误.但它保证终止.如果需要进行错误处理,可能就足以检查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;
}
Run Code Online (Sandbox Code Playgroud)

在性能方面以及在便利性和可维护性方面,非常非常难以击败此解析例程.