C中的I / O更快

时间:2009-04-01 12:41:00

标签: c optimization io

我遇到一个问题,需要从控制台获取1000000行输入,如下所示。

0 1 23 4 5
1 3 5 2 56
12 2 3 33 5
...
...

我使用过scanf,但速度非常慢。无论如何以更快的方式从控制台获取输入?我可以使用read(),但我不确定每行中的字节数,所以我不能读取()来读取'n'字节。 谢谢, 非常有责任

8 个答案:

答案 0 :(得分:6)

使用fgets(...)一次拉一行。请注意,您应该检查行末尾的'\ n',如果没有,则要么处于EOF状态,要么需要读取另一个缓冲区的值,并将两者连接在一起。泡沫,冲洗,重复。不要被缓冲区溢出所困扰。

然后,您可以自己解析内存中的每条逻辑行。我喜欢使用strspn(...)和strcspn(...)来做这类事情,但你的里程可能会有所不同。

解析: 定义分隔符字符串。使用strspn()计算与分隔符匹配的“非数据”字符,并跳过它们。使用strcspn()来计算与分隔符不匹配的“数据”字符。如果此计数为0,则表示已完成(行中不再有数据)。否则,将那些N个字符复制到一个解析函数,如atoi(...)或sscanf(...)。然后,将指针基址重置为此块的末尾,并重复skip-delims,copy-data,convert-to-numeric过程。

答案 1 :(得分:3)

您使用具有固定大小缓冲区的多个read,直到您点击文件末尾。

答案 2 :(得分:3)

如果您的示例具有代表性,您确实每行有一个固定格式的五位十进制数,我可能会使用fgets()的组合来读取行,然后调用strtol()的循环从字符串转换为整数。

这应该比scanf()更快,同时比自己进行字符串到整数转换更清晰,更高级。

这样的事情:

typedef struct {
  int number[5];
} LineOfNumbers;

int getNumbers(FILE *in, LineOfNumbers *line)
{
  char buf[128];  /* Should be large enough. */
  if(fgets(buf, sizeof buf, in) != NULL)
  {
    int i;
    char *ptr, *eptr;

    ptr = buf;
    for(i = 0; i < sizeof line->number / sizeof *line->number; i++)
    {
      line->number[i] = (int) strtol(ptr, &eptr, 10);
      if(eptr == ptr)
        return 0;
      ptr = eptr;
    }
    return 1;
  }
  return 0;
}

注意:这是未经测试的(甚至是未编译的!)浏览器编写的代码。但也许作为一个具体的例子很有用。

答案 3 :(得分:2)

如果可以,请使用二进制I / O.文本转换可以将读数减慢几个。如果您正在使用文本I / O,因为它易于调试,请再次考虑二进制格式,并使用od程序(假设您使用的是unix),以便在需要时使其易于阅读。

哦,另一件事:AT&amp; T的SFIO库代表更安全/更快的文件IO。你可能也有一些运气,但我怀疑你会得到与二进制格式相同的加速。

答案 4 :(得分:1)

一次读取一行(如果缓冲区对于一行不够大,则扩展并继续使用更大的缓冲区)。

然后使用专用函数(例如atoi)而不是一般转换。

但是,最重要的是,设置一个可重复的测试工具,并进行性能分析,以确保更改确实可以加快速度。

答案 5 :(得分:0)

出于好奇,在控制台中生成那么多行的是什么?

答案 6 :(得分:0)

如果你试图读取的字节多于

,那么

fread仍然会返回。

我发现读取文件的最快方法是这样的:

/ *寻求文件结尾* / FSEEK(文件,0,SEEK_END);

/ *获取文件大小* / size = ftell(file);

/ *寻求文件的开头* / FSEEK(文件,0,SEEK_SET);

/ *为文件制作缓冲区* / buffer = malloc(1048576);

/ *一次以1MB为单位,直到达到大小字节等* /

在现代计算机上使用你的ram并将整个东西加载到ram中,然后你可以轻松地通过内存工作。

至少你应该使用尽可能大的块大小的fread,并且至少与缓存块或HDD扇区大小一样大(最小4096字节,我个人最少使用1048576)。您会发现,通过更大的读取请求,rfead能够在一次操作中顺序获得大流。一些人使用128字节的建议是荒谬的....因为你最终会得到驱动器必须一直寻找,因为呼叫之间的微小延迟将导致头已经过了下一个扇区几乎可以肯定有你想要的顺序数据。

答案 7 :(得分:0)

使用fread()fread_unlocked()(如果您的程序是单线程的)输入,可以大大减少执行时间。锁定/解锁输入流只需要一次,所以忽略它。

以下是代码:

#include <iostream>

int maxio=1000000;
char buf[maxio], *s = buf + maxio;

inline char getc1(void)
{
   if(s >= buf + maxio) { fread_unlocked(buf,sizeof(char),maxio,stdin); s = buf; }
   return *(s++);
}
inline int input()
{
   char t = getc1();
   int n=1,res=0;
   while(t!='-' && !isdigit(t)) t=getc1(); if(t=='-')
   {
      n=-1; t=getc1();
   }
   while(isdigit(t))
   {
     res = 10*res + (t&15);
     t=getc1();
   }
   return res*n;
}

这是在C++中实施的。在C中,您不需要包含iostream,函数isdigit()是隐式可用的。

您可以通过调用getc1()将输入视为字符流,并通过调用input()获取整数输入。

使用fread()背后的整个想法是立即获取所有输入。调用scanf()/printf(),重复占用锁定和解锁流的宝贵时间,这在单线程程序中是完全冗余的。

同时确保maxio的值使得所有输入只能在几个“往返”中进行(在这种情况下理想情况下为1)。必要时调整它。

希望这有帮助!