Есть идеи по сокращению времени компиляции boost :: spirit?
Я только что портировал гибкий парсер для boost :: spirit. EBNF имеет около 25 правил.
Результат работает хорошо, и производительность во время выполнения хорошая.
Проблема в том, что компиляция требует вечности! Это занимает около десяти минут и требует почти гигабайт памяти. Исходный синтаксический анализатор гибкости скомпилирован за несколько секунд.
Я использую ускоренную версию 1.44.0 и Visual Studio 2008.
В статье Джоэля де Гусмана «Лучшие практики» говорится, что
Правила со сложными определениями сильно вредят компилятору. Мы видели правил, которых больше ста строк и займет пару минут на компиляцию
Что ж, у меня нет ничего и близкого к этому, но моя компиляция по-прежнему занимает больше пары минут
Вот самая сложная часть моей грамматики. Можно ли каким-то образом разбить его на более мелкие части?
rule
= ( tok.if_ >> condition >> tok.then_ >> *sequel ) [ bind( &cRuleKit::AddRule, &myRulekit ) ]
| ( tok.if_ >> condition >> tok.and_ >> condition >> tok.then_ >> *sequel ) [ bind( &cRuleKit::AddRule, &myRulekit ) ]
;
condition
= ( tok.identifier >> tok.oper_ >> tok.value ) [ bind( &cRuleKit::AddCondition, &myRulekit, _pass, _1, _2, _3 ) ]
| ( tok.identifier >> tok.between_ >> tok.value >> "," >> tok.value ) [ bind( &cRuleKit::AddConditionBetween, &myRulekit, _pass, _1, _3, _4 ) ]
;
sequel
= ( tok.priority_ >> tok.high_ ) [ bind( &cRuleKit::setPriority, &myRulekit, 3 ) ]
| ( tok.priority_ ) [ bind( &cRuleKit::setPriority, &myRulekit, 2 ) ]
| ( tok.interval_ >> tok.value ) [ bind( &cRuleKit::setInterval, &myRulekit, _2 ) ]
| ( tok.mp3_ >> tok.identifier ) [ bind( &cRuleKit::setMP3, &myRulekit, _2 ) ]
| ( tok.disable_ ) [ bind( &cRuleKit::setNextRuleEnable, &myRulekit, false ) ]
;
Комментируя части грамматики, я обнаружил, с какой частью компилятор тратил больше всего времени.
set_reading
= tok.set_reading >> +attribute_reading
;
attribute_reading
= ( tok.name_ >> tok.identifier )
[
bind( &cPackage::Add, &myReadings, _pass, _2 )
]
| ( tok.nmea_ >> tok.identifier )
[
bind( &cPackage::setNextNMEA, &myReadings, _2 )
]
| ( tok.column_ >> tok.integer )
[
bind( &cPackage::setNextColumn, &myReadings, _2 )
]
| ( tok.precision_ >> tok.value )
[
bind( &cPackage::setNextPrecision, &myReadings, _2 )
]
| ( tok.unit_ >> tok.identifier )
[
bind( &cPackage::setNextUnit, &myReadings, _2 )
]
| ( tok.value_ >> tok.identifier )
[
bind( &cPackage::setNextValue, &myReadings, _2 )
]
| ( tok.qualifier_ >> tok.identifier >> tok.qual_col_ >> tok.integer )
[
bind( &cPackage::setNextQualifier, &myReadings, _2, _4 )
]
;
Я бы не назвал это сложным, но это, безусловно, самое длинное правило. Поэтому я подумал, что попробую разделить его, вот так:
set_reading
= tok.set_reading >> +attribute_reading
;
attribute_reading
= attribute_reading_name
| attribute_reading_nmea
| attribute_reading_col
| attribute_reading_precision
| attribute_reading_unit
| attribute_reading_value
| attribute_reading_qualifier
;
attribute_reading_name
= ( tok.name_ >> tok.identifier ) [ bind( &cPackage::Add, &myReadings, _pass, _2 ) ]
;
attribute_reading_nmea
= ( tok.nmea_ >> tok.identifier ) [ bind( &cPackage::setNextNMEA, &myReadings, _2 ) ]
;
attribute_reading_col
= ( tok.column_ >> tok.integer ) [ bind( &cPackage::setNextColumn, &myReadings, _2 ) ]
;
attribute_reading_precision
= ( tok.precision_ >> tok.value ) [ bind( &cPackage::setNextPrecision, &myReadings, _2 ) ]
;
attribute_reading_unit
= ( tok.unit_ >> tok.identifier ) [ bind( &cPackage::setNextUnit, &myReadings, _2 ) ]
;
attribute_reading_value
= ( tok.value_ >> tok.identifier ) [ bind( &cPackage::setNextValue, &myReadings, _2 ) ]
;
attribute_reading_qualifier
= ( tok.qualifier_ >> tok.identifier >> tok.qual_col_ >> tok.integer ) [ bind( &cPackage::setNextQualifier, &myReadings, _2, _4 ) ]
;
Это сэкономит несколько минут от общего времени компиляции !!!
Как ни странно, пиковая потребность в памяти остается прежней, просто требуется меньше времени
] Итак, я чувствую немного больше надежды, что все мои усилия по изучению boost :: spirit будут стоящими.
Я действительно думаю, что это немного странно, что компилятор нуждается в таком тщательном управлении. Я бы подумал, что современный компилятор заметил бы, что это правило было просто списком независимых правил ИЛИ.
Я потратил большую часть семи дней на изучение boost :: spirit и портирование небольшого, но реального парсера из гибкий. Я пришел к выводу, что это работает, и код очень элегантный. К сожалению, наивное использование путем простого расширения кода примера учебника для реального приложения быстро перегружает компилятор - память и время, затрачиваемые на компиляцию, становятся совершенно непрактичными. Очевидно, есть методы для решения этой проблемы, но они требуют тайных знаний, на изучение которых у меня нет времени. Думаю, я буду придерживаться гибкости, которая может быть уродливой и старомодной, но относительно проста и молниеносна.