我正在考虑移植使用从ANTLR3到ANTLR4的C ++运行时的JavaScript语法(从技术上讲,ANTLR3版本使用C运行时,但现在不相关)。
我正在努力解决的主要问题是表现。我有几个性能问题阻止我得到甚至接近0的开销,但我将专注于这个线程中最严重的一个:构建完整的缓存DFA并解析命中的文件似乎非常困难未缓存的过渡非常缓慢。
我创建了一个包含16个文件的小型测试套件,并分解了热备和测试阶段。 (Antlr3并不需要热身,但无论如何)
当预热设置===测试集(每个16个文件)
Test ms
------------------
Antlr3Warmup 338
Antlr4Warmup 55860
Antlr3Test 335
Antlr4Test 439
看起来很近吗?现在让我们看看如果集合是不相交的(每个8个文件)会发生什么
Test ms
------------------
Antlr3Warmup 78
Antlr4Warmup 32032
Antlr3Test 273
Antlr4Test 24356
显然,在32秒内它甚至没有接近构建DFA - 我们刚刚构建足以在相同文件上获得良好性能。我可以尝试在预热套件中添加更多文件,但性能似乎永远不会提高,并且需要大约15米才能通过更大的热身套件(例如jquery + angular + react)。分析执行表明确实瓶颈是ParserATNSimulator::computeTargetState
。
发生了什么事?有没有更智能的方法来构建完整的DFA缓存?例如。 Parser::buildCompleteDFA()
。
对于某些上下文,我从https://github.com/antlr/grammars-v4/blob/master/javascript/JavaScriptParser.g4开始并重构了singleExpression规则以使整个语法SLL(在此之前,性能似乎更糟)。
编辑:伊万建议左递归重构可能是罪魁祸首。所以这里是没有重构的相同基准(语法现在是LL):
当预热设置===测试集(每个16个文件)
Benchmark ms
--------------------
Antlr3Warmup 337
Antlr4Warmup 4224
Antlr3Test 311
Antlr4Test 1785
ANTLR4在完全缓存的文件上失去了优势,但非缓存文件的性能有所提升。在构建DFA缓存后,它会在长时间内发挥作用吗?
Benchmark ms (same as before) ms (extra warmup)
--------------------
Antlr3Warmup 77 77
Antlr4Warmup 455 47649
Antlr3Test 275 274
Antlr4Test 3750 3559
没那么多。我敢打赌,越来越长的热身会改善表现,但是之前测试的1785个数字是下限。
编辑2:这里有一些关于下限如此之高的分析数据:
Time spent w/ ATN transition logic: 12%
Time spent accessing the DFA: 16%
Time spent w/ shared_ptr: 27%
Time spent w/ dynamic_casts: 33%
(这已经在我已经删除了一些dynamic_casts之后。实际上这些都是向下转发,但我想LLVM无法很好地优化它)
另外,即使同一个文件被运行两次,我仍然会看到对computeReachSet
的调用!在SLL语法中,解析器adaptivePredict
只会调用getExistingTargetState