Pyparsing packrat会降低performan的速度

2024-05-18 15:34:33 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在寻找一种方法来提高我用pyparsing构建的解析器的性能。我阅读了packrat解析,这似乎真的有助于提高解析器的性能。但是,当我启用packrat解析时,性能变差了!如果没有packrat,解析一个20 MB的文件大约需要2分钟。加冰2-3次。我读到packrat在parseActions上可能会有问题,所以我从语法中删除了所有parseActions,看看packrat是否会提高它的性能,但这也没有帮助。我尝试了不同的缓存大小限制(不受限制,范围在100-1000之间),但是当我启用packrat时,所有这些方法反而降低了解析器的性能。在

有没有一个经验法则来设置packrat的缓存大小限制?有没有什么语法结构限制了packrat解析的使用,或者解释了为什么我的解析器性能变差了?在


Tags: 文件方法解析器语法mb经验pyparsing性能
1条回答
网友
1楼 · 发布于 2024-05-18 15:34:33

不管怎样,一个20MB的文件都需要一些时间来处理pyparsing,但是有些构造可能会减慢速度。在

当我们重新设计packrat解析的实现以使用OrderedDict作为缓存时,我对缓存大小的不同值进行了一些测试。结果发现,默认值128的效率仅比无限缓存低1-2%,在内存和性能上都有巨大的优势。因此,如果值超过200或300会对缓存有很大帮助,同时在缓存搜索和内存管理方面付出代价,我会感到惊讶的。在

当您通过在输入字符串的同一位置重新访问同一表达式来获得缓存命中时,Packrat解析效果最好。重要的是,它是精确的相同的表达式,而不仅仅是一个等价的表达式。例如,如果你有类似的东西:

Literal("A") + Optional("," + Literal("B")) + Optional("," + Literal("C"))

分隔逗号的独立文本是不同的表达式,因此不会是缓存命中。相反,如果为公共标点定义并重用单个表达式,则packrat解析更有可能从缓存中查找其解析结果,而不是重新分析:

^{pr2}$

最近我一直在使用expr()语法来创建expr的副本,这样诸如解析操作、空白设置等修饰符就不会被意外地全局更改。这将不利于表达式重用,因此如果您有许多不需要的expr()实例,那么只需重用基expr。还请注意,带有结果名称的表达式会生成隐式副本,因此请确保不要过度使用结果名称。在

Packrat解析在使用infixNotation时是最好的,尤其是当有6个或更多级别的运算符优先级时。infixNotation的表达式生成重用子表达式,而不是定义新的子表达式,因此它能够获得更好的缓存性能。在

您可能对Or过度使用了“^”运算符,而对MatchFirst使用了“|”运算符。在递归表达式(使用Forward)中使用时,这可能会特别昂贵。比较这两个表达式:

arith_operand = integer_expr ^ float_expr ^ variable_name ^ Keyword("pi") ^ Keyword("e")

通过使用“^”,所有表达式都将得到求值,然后选择最长的匹配项。特别是,如果解析“3.1416”,这是必要的,因为前导的“3”将匹配integer,但较长的“3.1416”将匹配float_expr。保证关键字和“我们的名字”匹配,但也没有匹配“变量”。(我们对variable_name和“pi”和“e”有相同的表达式屏蔽问题,因为它们可能会匹配为一个可能的变量名。)但是如果我们改为使用“|”,那么我们的解析器将在第一个匹配项上短路。我们只需要注意分析的顺序:

arith_operand = float_expr | integer_expr | Keyword("pi") | Keyword("e") | variable_name

也就是说,我们必须确保在匹配整数之前先检查浮点匹配,因为浮点的前导部分可能会被误认为是整数。在

如果你有很容易互斥的替代词(比如一系列可能的关键字,由于它们的关键字性,它们永远不会相互遮掩),那么你也可以根据某种可能的频率来对它们进行排序。例如:

bad_expr = Keyword("xylophone") | Keyword("the") | Keyword("a")

在大多数非音乐应用程序中可能会成为性能的失败者。在

在pyparsing的早期,我没有Regex类,因此我必须使用Combine(Word(nums) + "." + Optional(Word(nums)))之类的东西为实数定义一个表达式。对于pyparsing来说,这是一项大量的工作,而您只需使用Regex(r"\d+\.\d*")。通常,实数表达式实际上是低级表达式,在给定的解析运行中被使用和测试了数千次,因此转换为Regex确实值得。或者使用pyparsing_common中的一个数值表达式,比如pyparsing_common.real或{}。不过,您不需要太过火—pyparsing在内部使用正则表达式来匹配使用WordoneOf的表达式。在

您还可以直接检查缓存和缓存统计信息,ParserElement.packrat_cache和{}。cac公司统计数据是一个两个元素的列表,元素0是缓存命中数,元素1是未命中数。您可以为将输出缓存统计信息的循环表达式定义调试操作。您还可以使用Counter:Counter(str(key[0]) for key in ParserElement.packrat_cache)查找重复的表达式。通过str-操作表达式,它将有助于识别重复项。因此您可以查看不同缓存大小值的缓存效率。在

编辑:我的错误是,ParserElement.packrat_cache中缓存键的迭代不起作用,缓存OrderedDict本身被隐藏起来,不受外部访问。在

相关问题 更多 >

    热门问题