比fgets更快地读取一行输入?

13

我正在编写一个程序,性能很重要但不是至关重要。当前,我正从一个FILE*逐行读取文本,并使用fgets获取每一行。经过一些性能工具的测试,我发现我的应用程序在运行时有20%到30%的时间处于fgets内。

有更快的方法来获取一行文本吗? 我的应用程序是单线程的,没有使用多个线程的意图。输入可能来自stdin或文件。提前感谢您的帮助。


你的程序解析的行的平均长度(和可能的标准差)是多少?这有助于确定访问它们的最快方式。 - Juliano
@Juliano,每行代码长度始终小于260个字符。我已经避免了循环构建行。 - dreamlax
你能控制输入格式吗?你能让它更紧凑一些吗? - Dave
@Dave,不,我无法控制输入格式。 - dreamlax
7个回答

8

你没有说明你所在的平台,但如果是类UNIX系统,那么你可以尝试使用read()系统调用,它不会执行fgets()等函数所做的额外缓冲层。这可能会略微加快速度,另一方面也可能会减慢速度——唯一的办法就是尝试并查看结果。


最终结果证明这是所有方法中最快的。我最终选择了这条路。自己实现"缓冲处理"比我想象的要简单得多,而且速度比使用fgets()快得多(近4倍)。 - dreamlax

4
  1. 使用fgets_unlocked()函数,但请先仔细阅读其说明

  2. 使用fgetc()或fgetc_unlocked()函数获取数据,而不是使用fgets()函数。使用fgets()函数会导致数据被复制到内存两次:首先由C运行时库从文件中复制到一个内部缓冲区(流I/O是缓冲的),然后从该内部缓冲区复制到程序中的数组。


谢谢您的建议,但我忘了提到我正在使用Mac OS X。由于它是GNU扩展,因此无法使用fgets_unlocked。我将研究使用fgetc_unlocked。 - dreamlax
嗯,OS X正在运行GCC,你应该得到GNU扩展,对吧? - Martin Cote
2
@Martin:它不是GNU编译器的扩展,而是GNU C运行时库。 - dreamlax

4

一次性将整个文件读入缓冲区。

从该缓冲区处理行。

这是最快的解决方案。


3
你可以尝试通过将大量数据读入RAM,然后在RAM上操作来最小化从磁盘读取数据的时间。从磁盘读取数据是很慢的,所以通过一次性(理想情况下)读取整个文件,然后在RAM上操作来最小化这样做的时间。
有点像CPU缓存减少CPU实际返回RAM的时间,你可以使用RAM来最小化实际访问磁盘的次数。

我认为是这样,但我确定它不到一兆字节,所以阅读超过那个大小仍然会有帮助。 - GManNickG

2
如果数据来自磁盘,可能会受到IO限制。如果是这种情况,可以使用更快的磁盘(但首先要检查是否已充分利用现有磁盘...因为一些Linux发行版默认不优化磁盘访问(`hdparm`)),预先将数据暂存到内存中(例如将其复制到RAM磁盘中),或者准备好等待。
如果你没有IO限制,那么你可能会浪费很多时间在复制上。你可以从所谓的零拷贝方法中受益。比如通过内存映射文件,只通过指针访问它。
这超出了我的专业范围,所以你应该阅读一些资料或等待更有经验的帮助。
顺便说一句——你可能会陷入比问题本身更值得的工作中;也许一个更快的机器可以解决你所有的问题...
注意——不清楚是否可以内存映射标准输入...

有时它来自磁盘,有时则通过stdin输入,但在这两种情况下,fgets所花费的时间大致相同。即使为文件创建RAM磁盘也不能显著加快速度。 - dreamlax
修改后:问题在于这个应用程序将在最终用户的计算机上运行,因此性能非常重要。 - dreamlax

2

根据您的环境,使用setvbuf()来增加文件流使用的内部缓冲区大小可能会或可能不会提高性能。

下面是语法 -

setvbuf (InputFile, NULL, _IOFBF, BUFFER_SIZE);

输入文件InputFile是一个使用fopen()函数刚打开的FILE*文件指针,BUFFER_SIZE是缓冲区大小(由此调用为您分配)。

您可以尝试不同的缓冲区大小,以查看是否有任何积极影响。请注意,这完全是可选的,您的运行时可能根本不使用此调用。


0
如果操作系统支持,您可以尝试异步文件读取,即在 CPU 忙于其他任务时将文件读入内存。因此,代码大致如下:
start asynchronous read
loop:
  wait for asynchronous read to complete
  if end of file goto exit
  start asynchronous read
  do stuff with data read from file
  goto loop
exit:

如果您有多个CPU,则一个CPU读取文件并将数据解析成行,而另一个CPU接收每一行并处理它。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接