方程解析器效率

时间:2011-09-20 15:11:47

标签: c++ performance parsing equation

我将大约一个月的全职时间沉入本机C ++方程解析器中。它工作,除了它很慢(比硬编码方程慢30-100倍)。我可以改变什么来加快速度?

我阅读了有关高效代码的所有内容。粗略地说:

  • 解析器将字符串方程表达式转换为“操作”对象列表。
  • 操作对象有两个函数指针:“getSource”和“evaluate”。
  • 为了评估一个等式,我所做的只是操作列表上的for循环,依次调用每个函数。

在计算方程时没有遇到单个if / switch - 所有条件语句在最初分配函数指针时由解析器处理。

  • 我尝试了内联函数指针指向的所有函数 - 没有改进。
  • 从函数指针切换到仿函数会有帮助吗?
  • 如何删除函数指针框架,而是创建一整套派生的“操作”类,每个类都有自己的虚拟“getSource”和“evaluate”函数? (但这不是将函数指针移动到vtable中吗?)

我有很多代码。不知道要提炼/发布什么。询问它的某些方面,你们就会收到。

6 个答案:

答案 0 :(得分:5)

在你的帖子中,你没有提到你已经分析了代码。如果我在你的鞋子里,这是我要做的第一件事。它会让您清楚地了解花费的时间以及优化工作的重点。

答案 1 :(得分:1)

很难从描述中判断缓慢是否包括解析,或者只是解释时间。

解析器,如果你把它写成递归下降(LL1)应该是I / O绑定。换句话说,解析器读取字符以及解析树的构造应该比将文件简单地读入缓冲区花费的时间少得多。

解释是另一回事。 除非基本操作本身很长,否则解释和编译代码之间的速度差异通常要慢10-100倍。 也就是说,你仍然可以优化它。

您可以进行分析,但在这种简单的情况下,您还可以在调试器中单个程序单步执行单个指令级别。 这样,你“走在电脑前”,很明显可以改进的地方。

每当我正在做你正在做的事情,也就是说,为用户提供一种语言,但我希望语言能够快速执行,我所做的就是: 我将源语言翻译成我有编译器的语言,然后将其即时编译成.dll(或.exe)并运行它。 它非常快,我不需要写一个翻译或担心它的速度有多快。

答案 2 :(得分:0)

首先是:描述实际出错的地方。解析或评估中的瓶颈是什么? valgrind提供了一些可以帮助你的工具。

如果是解析,boost :: spirit可能对你有所帮助。如果在评估中,请记住虚拟函数的评估速度可能很慢。我已经用递归boost :: variant's做了很好的经验。

答案 3 :(得分:0)

你知道,构建一个表达式递归下降解析器真的很容易,表达式的LL(1)语法只是几个规则。解析然后变成线性事件,其他一切都可以在表达式树上工作(基本上解析);您将从较低节点收集数据并将其传递到较高节点进行聚合。

这样可以完全避免在运行时确定调用路径的函数/类指针,依赖于经验证的递归性(或者,如果您愿意,可以构建迭代LL解析器)。

答案 4 :(得分:0)

您似乎正在使用非常复杂的数据结构(据我所知,带有指针的语法树等)。因此,遍历指针解引用并不是非常有效的内存方式(大量随机访问)并且可能会显着降低您的速度。正如Mike Dunlavey所提出的,您可以在运行时使用其他语言或嵌入编译器(如LLVM)编译整个表达式。据我所知,Microsoft .Net使用Reflection.Emit和Linq.Expression树提供此功能(动态编译)。

答案 5 :(得分:0)

这是我建议反对分析的罕见时刻之一。我的直接猜测是你使用的基本结构是问题的真正根源。在您合理地确定基本结构是合理的之前,对代码进行概要分析很少,而且主要是找到可以改进基本结构的哪些部分。当你真正需要做的就是抛弃你拥有的大部分内容并且基本上重新开始时,它并没有那么有用。

我建议将输入转换为RPN。要执行此操作,您需要的唯一数据结构是堆栈。基本上,当你到达一个操作数时,你将它推到堆栈上。当您遇到操作员时,它会对堆栈顶部的项目进行操作。当你完成对一个结构良好的表达式的评估时,你应该在栈上只有一个项目,这是表达式的值。

通常提供比这更好的性能的唯一事情是像@Mike Dunlavey建议的那样,只需生成源代码并通过“真正的”编译器运行它。然而,这是一个相当“沉重”的解决方案。如果你真的需要最高速度,它显然是最好的解决方案 - 但如果你只是想改进你现在正在做的事情,转换到RPN并解释它通常可以为少量代码提供相当不错的速度提升。 / p>

相关问题