比scanf更快?

时间:2011-12-12 23:46:22

标签: 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;

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

4 个答案:

答案 0 :(得分:7)

我遇到的开销不是解析本身,而是对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;
}

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

答案 1 :(得分:4)

通过缓冲,您将能够显着改善您的示例 - 将大量字符读入内存,然后从内存中解析它们。

如果您正在从磁盘读取数据,则缓冲区可能会因块大小的倍数而增加性能。

编辑:您可以让内核为您处理此问题,方法是使用mmap将文件映射到内存中。

答案 2 :(得分:1)

这是我使用的东西。

 #define scan(x) do{while((x=getchar())<'0'); for(x-='0'; '0'<=(_=getchar()); x=(x<<3)+(x<<1)+_-'0');}while(0)
 char _;

但是,这仅适用于整数。

答案 3 :(得分:-1)

根据你的说法,我得出以下事实:

  • 数字在0-99范围内,占10 + 100个不同的字符串(包括前导零)
  • 您相信您的输入流符合某种规范,并且不会包含任何意外的字符序列

在这种情况下,我会使用查找表将字符串转换为数字。给定一个字符串s [2],查找表的索引可以由s[1]*10 + s[0]计算,交换数字并利用ASCII中'\0'等于0的事实。

然后,您可以通过以下方式阅读输入:

// given our lookup method, this table may need padding entries
int lookup_table[] = { /*...*/ };

// no need to call superfluous functions
#define str2int(x) (lookup_table[(x)[1]*10 + (x)[0]])

while(read_token_from_stream(stdin, buf))
        next_int = str2int(buf);

在今天的机器上,很难想出更快的技术。我的猜测是,这种方法的运行速度可能比任何基于scanf()的方法快2到10倍。