fscanf 和 sscanf 的速度

The speed of fscanf and sscanf

对于 C 作业,我应该将一个大文本文件中的单词分解并一个一个地处理。基本上,一个词是字母表的任何线性序列。因为,这将是我程序的瓶颈,所以我想尽可能快地完成这个过程。

我的想法是使用扫描函数格式说明符 ([a-zA-z]) 将文件中的单词扫描到字符串缓冲区中。如果缓冲区已满,我会检查文件中是否还有更多字母表(基于文件指针所在的位置)。如果有,那么我会增加缓冲区大小并继续将更多字母复制到缓冲区中,直到我遇到非字母。

问题是我使用的是fscanf还是sscanf(将整个文件复制到一个字符串中)。一个比另一个更快还是有更好的替代方案?

你的问题几乎跑题了,因为它需要基于意见的答案。

了解一种方法与另一种方法相比有多快的唯一方法是尝试两种方法并测量生成的可执行文件在真实数据上的性能。

以当今普通 PC 的计算能力,需要非常 的大文件来衡量实际性能差异。

所以请继续实施您的想法。您似乎很了解潜在的性能瓶颈,将这些想法转化为实际的 C 代码。针对此问题提供 2 个不同但正确的程序以及性能分析应该会让您获得 A+。作为雇主,我很重视这种方法。

PS:恕我直言,大部分时间都花在从文件系统获取数据上。如果文件大于可用内存,那应该是你的瓶颈。如果文件适合操作系统文件系统缓存,后续基准测试应该会比第一个基准测试提供更好的性能...

如果您被允许编写系统特定代码,请尝试使用 mmap 和简单的 for 循环,通过在 mmapped char 数组上查找表进行显式测试。

正如 Heto 在评论中指出的那样,这里的主要瓶颈可能是从磁盘读取文件,而不是您决定使用的任何 scanf 函数变体。

如果你真的想加速你的应用程序,你应该尝试构建一个管道。当您现在描述应用程序时,您基本上会分两个阶段工作:将文件读入缓冲区,并从缓冲区解析单词。

如果您决定将整个文件读入一个字符串,然后在该字符串上使用 sscanf,那么 activity 可能如下所示:

reading: ████████████████
parsing:                 ████████████████

如果直接在文件上使用 fscanf,你会得到一些不同的东西,因为你不断地在读取和解析之间切换:

reading: █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █
parsing:  █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █

在这两种情况下,您最终花费的时间大致相同。

但是,如果您可以i/o 异步处理您的文件,那么您可以将等待磁盘数据的时间与用于计算的时间重叠。理想情况下,您最终会得到这样的结果:

reading: ████████████████
parsing:  ████████████████

我的图可能不是那么准确(我们已经指出,解析应该比 i/o 花费更少的时间,所以这两个条的长度真的不应该相同)——但是您应该了解总体思路。如果您可以设置一个从处理中异步读取数据的管道,那么您可以通过重叠通信(从磁盘读取)和计算(解析)来获得很大的加速。

您可以使用 POSIX asynchronous I/O (aio) 实现这样的异步管道,或者只使用两个线程进行简单的 producer/consumer 设置(其中一个从文件中读取,另一个执行解析)。


老实说,除非您正在处理 大量 文本文件,否则您可能几乎无法衡量您所采用的任何可能方法之间的速度差异可能会选择...

这种流水线方法更适用于计算密集型操作(不仅仅是扫描字符),并且通信延迟较高(例如数据通过网络而不是本地磁盘传输时) .但是,探索不同的选项仍然是一个很好的练习。毕竟,无论如何,作业都是人为设计的——重点是学习一些有用的东西,你以后可能会在实际项目中使用,对吧?


单独说明一下,使用任何 scanf 可能比仅仅遍历缓冲区以提取字符串 [A-Za-z] 慢。这是因为,对于任何 scanf 函数,代码首先需要 解析您的格式字符串 以确定您要查找的内容,然后实际解析输入.有时编译器可以做一些聪明的事情——比如 gcc 通常如何将没有格式说明符的 printf 改为 puts——但我认为 scanf 和朋友们没有这样的优化,特别是如果你使用像 %[A-Za-z] 这样的特殊格式而不是像 %d.

这样的标准格式说明符