我正在PLY中使用一个相当简单的解析器,我的一个规则采用以下形式:
def p_things(p):
'''
things : thing things
things : thing
'''
p[0] = [p[1]]
if len(p) == 3:
p[0] += p[2]
输入文件通常是thing
s的简单列表,因此解析本身并不复杂。但是我的一些输入文件非常大(相当规律地超过100,000行,极端情况下超过1,000,000行)。在分析中(通过cProfile and pstats),通过重复调用p_things
来占用大部分运行时 - 可能是对things
列表中的每个项目进行一次调用。
有没有办法减少这个时间,或者更有效的方法来构建这个规则?到目前为止我看到的大多数答案(以及我发现的规范编译器信息)都将此方法列为构建可解析项列表的普遍接受的方法,无论长度如何。
答案 0 :(得分:8)
原来我忘记了一些基本的编译器理论。 PLY是一个LALR(1)解析器,因此最好将规则写为:
def p_things(p):
'''
things : things thing
things : thing
'''
if len(p) == 2:
p[0] = [p[1]]
else:
p[0] = p[1]
p[0].append(p[2])
虽然它可能看起来更冗长,但实际上有一个显着的改进 - 在PLY或Python的某个地方,解析器能够在左递归形式上应用一些优化。我看到我的较大输入文件的性能从指数下降到线性;一个样本,things
列表中有超过一百万个项目,在不到20%的时间内运行。
答案 1 :(得分:0)
在不更改代码的情况下,您可以尝试使用python的“PyPy”版本进行即时编译 - 它可能比普通的CPython更快地运行代码。
答案 2 :(得分:0)
所以总结一下:
+=
的使用有关)为了更好地了解Ioannis Filippidis' comment,更容易对其进行可视化显示。这就是我认为的意思,也差不多是我最终得到的结果。
def p_things_iter(p):
'''
things_iter : things thing
'''
p[0] = p[1]
p[0].append(p[2])
def p_things_end(p):
'''
things_iter : thing
'''
p[0] = [p[1]]
def p_things(p):
'''
things : things_iter
things : things_end
'''
p[0] = p[1]