Можно ли принудительно создать полный кэш DFA для грамматики ANTLR4?

Я смотрю на портирование грамматики JavaScript, которая использует среду выполнения C ++ из ANTLR3 в ANTLR4 (технически версия ANTLR3 использует среду выполнения C, но сейчас это не актуально).

Основная проблема, с которой я борюсь, это производительность. У меня есть несколько проблем с производительностью, которые мешают мне даже приблизиться к нулю, но я собираюсь сосредоточиться на наиболее серьезной проблеме в этой теме: кажется невероятно трудным создать полностью кэшированный 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 + реагировать). Выполнение профилирования показывает, что узкое место действительно 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. На самом деле все они являются downcast, но я думаю, что LLVM не может оптимизировать это хорошо)

Кроме того, я до сих пор вижу звонки computeReachSet даже для одного файла, запускаемого дважды! В грамматике SLL парсер adaptivePredict бы только позвонить getExistingTargetState

4

Решение

Задача ещё не решена.

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector