Минимизация времени компиляции boost :: spirit

Есть идеи по сокращению времени компиляции 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 и портирование небольшого, но реального парсера из гибкий. Я пришел к выводу, что это работает, и код очень элегантный. К сожалению, наивное использование путем простого расширения кода примера учебника для реального приложения быстро перегружает компилятор - память и время, затрачиваемые на компиляцию, становятся совершенно непрактичными. Очевидно, есть методы для решения этой проблемы, но они требуют тайных знаний, на изучение которых у меня нет времени. Думаю, я буду придерживаться гибкости, которая может быть уродливой и старомодной, но относительно проста и молниеносна.

27
задан Theolodis 26 June 2014 в 14:07
поделиться