C中的I / O更快
我有一个问题,将从控制台采取1000000行输入,如下所示。
0 1 23 4 5 1 3 5 2 56 12 2 3 33 5 ... ...
我使用过scanf,但它非常慢。 无论如何以更快的方式从控制台获取输入? 我可以使用read(),但我不确定每行中的字节数,所以我不能读取()来读取’n’字节。 谢谢,非常有责任
使用fgets(…)一次拉一行。 请注意,您应该检查行末尾的’\ n’,如果没有,则要么是EOF,要么是读取另一个缓冲区的值,并将两者连接在一起。 泡沫,冲洗,重复。 不要陷入缓冲区溢出。
那么,您可以自己解析内存中的每条逻辑行。 我喜欢使用strspn(…)和strcspn(…)来做这类事情,但你的里程可能会有所不同。
解析:定义分隔符字符串。 使用strspn()计算与分隔符匹配的“非数据”字符,并跳过它们。 使用strcspn()来计算与分隔符不匹配的“数据”字符。 如果此计数为0,则表示已完成(行中不再有数据)。 否则,将那些N个字符复制到一个解析函数,如atoi(…)或sscanf(…)。 然后,将指针基址重置为此块的末尾,并重复skip-delims,copy-data,convert-to-numeric进程。
您使用具有固定大小缓冲区的多个read
,直到您到达文件末尾。
如果您的示例具有代表性,那么您确实每行有一个固定格式的五位十进制数,我可能会使用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; }
注意:这是未经测试(甚至未编译!)浏览器编写的代码。 但也许有用的具体例子。
如果可以,请使用二进制I / O. 文本转换可以使读数减慢几倍 。 如果您正在使用文本I / O,因为它易于调试,请再次考虑二进制格式,并使用od程序(假设您使用的是unix),以便在需要时使其可读。
哦,另一件事:有AT&T的SFIO库,它代表更安全/更快的文件IO。 你可能也有一些运气,但我怀疑你会得到与二进制格式相同的加速。
一次读一行(如果缓冲区对于一行不够大,则扩展并继续使用更大的缓冲区)。
然后使用专用函数(例如atoi)而不是一般的转换。
但是,最重要的是,设置一个可重复的测试工具,并进行分析,以确保更改确实可以加快速度。
出于好奇,是什么产生了许多在控制台中快速的线条?
如果你试图读取更多的字节,fread仍然会返回。
我发现读取文件的最快方法是这样的:
/ *寻找文件结尾* / fseek(文件,0,SEEK_END);
/ *获取文件大小* / size = ftell(文件);
/ *寻找文件的开头* / fseek(file,0,SEEK_SET);
/ *为文件* / buffer = malloc(1048576)创建一个缓冲区;
/ *每次1MB,直到你达到大小字节等* /
在现代计算机上使用你的ram并将整个东西加载到ram中,然后你可以轻松地通过内存工作。
至少你应该使用尽可能大的块大小的fread,并且至少与缓存块或HDD扇区大小一样大(最小4096字节,我个人最少使用1048576)。 您会发现,通过更大的读取请求,rfead能够在一次操作中顺序获得大流。 一些人使用128字节的建议是荒谬的….因为你最终会得到驱动器必须一直寻找,因为呼叫之间的微小延迟将导致头已经过了下一个扇区几乎可以肯定拥有您想要的顺序数据。
通过使用fread()
或fread_unlocked()
(如果您的程序是单线程的)获取输入,可以大大减少执行时间。 锁定/解锁输入流只需要一次可以忽略不计的时间,所以请忽略它。
这是代码:
#include 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()
将输入作为字符流,并通过调用getc1()
获取整数输入。
使用fread()
的整个想法是立即获取所有输入。 调用scanf()/printf()
反复占用锁定和解锁流的宝贵时间,这在单线程程序中是完全冗余的。
还要确保maxio
的值是这样的,所有输入只能在几个“往返”中进行(在这种情况下理想情况下是一个)。 根据需要调整它。
希望这可以帮助!