【问题标题】:File buffering in lexers: is it advisable, now that the OS (and language libraries) already implement buffers internally?词法分析器中的文件缓冲:现在操作系统(和语言库)已经在内部实现了缓冲区,是否可取?
【发布时间】:2018-10-06 15:19:09
【问题描述】:

在两本书中提到了一种词法分析器优化技术:K.Cooper 等人的《Engineering a compiler》。和 C.Fischer 等人制作的编译器。这是第一本书(第 69 页)的例外:

虽然逐个字符的 I/O 会产生清晰的算法公式,但每个字符的过程调用开销相对于在表驱动或直接编码扫描仪中模拟 DFA 的成本是显着的。为了降低每个字符的 I/O 成本,编译器编写器可以使用缓冲 I/O,其中每个读取操作返回更长的字符串或缓冲区,然后扫描程序通过缓冲区索引。扫描器维护一个指向缓冲区的指针...

我的问题是,这种技术的意义是什么?既然操作系统通常已经实现了内存缓冲,那么为什么作者建议我们实现一个缓冲区呢? (此外,高级语言提供的标准库通常有一个由文件流处理例程维护的缓冲区,例如 C++ 的 std::ifstream)。

我知道在某些应用程序中,例如数据库系统,自定义缓冲区机制不仅是可取的(更了解访问模式),而且有时是必要的(恢复、日志记录)。类似的推理是否适用于编译器中的词法分析器?如果有,怎么做?

编辑: 这是一个类似的问题:link,但我想了解更多关于自定义缓冲区的参数(如支持数据库系统中的缓冲区的参数),如果有的话。

另一篇帖子here 比较了手动缓冲与 C++ 中的 std::fstream 缓冲。

【问题讨论】:

  • 所有应用程序都使用缓冲区。它不仅限于编译器编写者。自 1955 年左右以来,操作系统一直在缓冲。没有任何改变。每个字符的系统调用开销太高而无法支付。
  • @EJP 是的。但是,既然操作系统和语言库已经有了自定义缓冲区,那么它真的可以加快词法分析器的速度吗?
  • 操作系统和语言库总是拥有它。您的问题基于错误的假设。
  • @EJP 对不起,我不明白.. 我的假设是什么?
  • 您的假设是,关于缓冲的某些内容最近发生了变化,导致您书中的引用无效。您一直在说诸如“现在内存缓冲通常已经由操作系统实现”和“现在操作系统和语言库拥有它”之类的话。没有“现在”。他们一直都有。什么也没有变。从任何地方一次获取 > 1 个字符总是更快。你也在问为什么作者说现在他们做了一些事情,据说事情已经改变了,这毫无意义。

标签: compiler-construction


【解决方案1】:

类似的原因是否适用于编译器中的词法分析器?

有时,当然。例如,考虑一个词法分析器规则,它返回一个表示“这是一个变量名”的标记。解析器不仅需要“是变量名”标记本身,还需要实际名称:如果不知道该名称是 Fred、Wilma、Barney 还是其他任何名称,就知道这是一个名称是没有用的。

如果它一个名字,那么这个名字应该存储在哪里?您能否授予解析器直接访问包含名称本身的字节流的权限? (如果需要,需要多长时间?解析器需要多长时间?)

或者考虑一个字符串文字(无论它们具有什么语法)。组成字符串的字符存储在哪里?您可以直接授予对原始缓冲区的访问权限吗?

如果您拥有提供这些扫描时间缓冲区的数据结构,您可以授予对它们的有限访问权限,并且确切地知道借用字符​​串的生命周期可能是什么。根据更多细节,这可能会让您避免复制(至少有时)。如果您正在使用一些库提供的缓冲 I/O 例程,这些例程承诺一次只传递一个字符,那么您绝对不能授予此类访问权限。

【讨论】:

  • 我想你可能误解了我的问题。我说的是文件流读取例程中使用的缓冲,并且该缓冲区在填满时需要清除,因此它不能保证其中存储的任何内容的生命周期。但事实上,你在这里所说的是一个有效的观点,尽管是在不同的话题上。名称、文字等都需要存储在某个地方,以便编译器以后可以引用它们。
  • 例如,如果您查看 flex 的实现,您会发现尽管默认使用 C stdio(它自己缓冲以避免系统调用开销),但 flex 有自己的缓冲区和您可以在解析器中使用它们。通常,符号和字符串表将 intern(取自 Lisp 的术语,参见例如 clhs.lisp.se/Body/f_intern.htm)大多数名称和一些字符串的单个副本,并从那时起使用实习副本。这允许通过简单的指针比较进行名称相等性测试。
【解决方案2】:

正如其他人指出的那样,无论操作系统是否缓冲(确实如此),您的应用程序依赖它都是非常昂贵的,因为这些操作系统/文件系统缓冲区不在您的应用程序地址空间中。为什么?因为要让您的应用程序获取该数据,它通常需要通过多层调用才能到达操作系统缓冲区。如果您一次只针对 1 个字符/字节执行此操作,则会产生开销。

如果您使用的是 IO 库:出于性能原因,其中一些会或将“提前”读取并将操作系统调用降至最低。

另一方面,如果您在不利用库的情况下进行操作,那么强烈建议您设置缓冲 IO 功能,原因与其他库相同。

最后,编译的最终结果是一个可执行的东西。除非您不允许 IO 发生,否则出于相同的原因,您将希望让您的语言特定(假设自托管)运行时来提供缓冲 IO。如果您的运行时基于提供它的一种语言或一系列库,那么您应该很好。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-16
    • 1970-01-01
    • 2010-11-27
    相关资源
    最近更新 更多