Ответ 1
Я быстро прочитал все.
Мой профайлер быстро сказал мне, что построение грамматики и (особенно) объекта lexer заняло довольно много ресурсов.
Действительно, просто изменение одной строки в SpiritParser.cpp сохранено 40% времени выполнения 1 (от ~ 28 с до ~ 17 с):
lexer::Lexer lexer;
в
static const lexer::Lexer lexer;
Теперь
-
делает статичность грамматики подразумевающей ее безгражданность. Я сделал это через
- перемещение
position_begin
вqi::_a
(с помощьюqi::locals
) и -
передать его как унаследованный атрибут в соответствующие моменты
-
грамматики
EDDIGrammar
иValueGrammar
, напримерstart %= qi::eps [ qi::_a = qi::_r1 ] >> program;
-
а также отдельные правила из
ValueGrammar
, которые используются снаружи).
-
Это имело ряд субоптимальных побочных эффектов:
- Отладка правил закомментирована, потому что
lexer::pos_iterator_type
не имеет перегрузки по потоку вывода по умолчанию -
выражение
qi::position(position_begin)
было "подделано" с довольно сложной заменой:auto local_pos = qi::lazy ( boost::phoenix::construct<qi::position>(qi::_a) );
Это не кажется идеальным. (В идеале хотелось бы заменить
qi::position
на модифицированную настраиваемую директиву парсера, которая знает, как получить get begin_position из qi:: locals (?), Поэтому не нужно было бы призывать выражение парсера лениво?)
- перемещение
В любом случае применяя эти дальнейшие изменения 2 сбрил еще ~ 15% времени выполнения:
static const lexer::Lexer lexer;
static const parser::EddiGrammar grammar(lexer);
try {
bool r = spirit::lex::tokenize_and_parse(
position_begin, position_end,
lexer,
grammar(boost::phoenix::cref(position_begin)),
program);
Свободные идеи:
- Рассматривали ли вы создание статического лексера (Генерация статического анализатора)
- Рассматривали ли вы возможность использовать точки ожидания, чтобы потенциально уменьшить количество обратного отслеживания (примечание: я ничего не измерял в этой области).
- Рассматривали ли вы альтернативы для
Position::file
иPosition::theLine
? Копирование строк кажется более тяжелым, чем необходимо. Я предпочитаю хранитьconst char *
. Вы также можете посмотреть Boost Flyweight - Требуется ли предварительный провал внутри вашей директивы
qi::position
? - (Немного несерьезно: считаете ли вы портирование на Spirit X3? Кажется, обещают потенциальные преимущества в форме семантики движения.)
Надеюсь, что это поможет.
[1] При синтаксическом анализе всех тестовых примеров в test/cases/*.eddi
100 раз так (github):
for (int i=0; i<100; i++)
for (auto& fname : argv)
{
eddic::ast::SourceFile program;
std::cout << fname << ": " << std::boolalpha << parser.parse(fname, program, nullptr) << "\n";
}
С простыми
time ./test ../../test/cases/*.eddi | md5sum
С помощью md5sum
, действующего как проверка работоспособности.
[2] Я создал запрос на перенос с рефакторингом доказательной концепции здесь https://github.com/wichtounet/eddic/pull/52