使用boostxpressive降低性能

使用boostxpressive降低性能,boost,boost-spirit,boost-xpressive,Boost,Boost Spirit,Boost Xpressive,最近我一直在使用boostxpressive解析文件。这些文件每个都有10MB,将有几百个需要解析 Xpressive很好用,语法清晰,但性能会带来问题。令人难以置信的是,它在调试版本中是如何爬行的,而在发布版本中,它在每个文件上花费的时间超过一整秒钟。我已经针对旧的普通get_line()、find()和sscanf()代码进行了测试,它可以轻松击败xpressive 我知道类型检查、回溯等都有成本,但这对我来说似乎太过分了。我多么奇怪,我做错了什么?以合适的速度运行是否有任何优化方法?它是否

最近我一直在使用boostxpressive解析文件。这些文件每个都有10MB,将有几百个需要解析

Xpressive很好用,语法清晰,但性能会带来问题。令人难以置信的是,它在调试版本中是如何爬行的,而在发布版本中,它在每个文件上花费的时间超过一整秒钟。我已经针对旧的普通get_line()、find()和sscanf()代码进行了测试,它可以轻松击败xpressive

我知道类型检查、回溯等都有成本,但这对我来说似乎太过分了。我多么奇怪,我做错了什么?以合适的速度运行是否有任何优化方法?它是否应该努力将代码迁移到boost::spirit

我准备了一个lite版本的代码,其中嵌入了几行真实的文件,以防有人测试和帮助

注意-作为一项要求,必须使用VS2010(不幸的是,不完全符合c++11)

#包括
#包括
常量字符输入[]=“[2018-Mar-13 13:13:59.580482]-0.200秒=>驾驶员:0-速度:0.0-道路:BTN-1002-公里:90.0-打开:1\n\
[2018-Mar-13 13:13:59.580482]-0.200s=>驾驶员:0-速度:0.0-道路:A-11-公里:90.0-坡度:0\n\
[2018-Mar-13 13:14:01.170203]-1.790 s=>驾驶员:0-速度:0.0-道路:A-11-公里:90.0-档位:0\n\
[2018-Mar-13 13:14:01.170203]-1.790 s=>驾驶员:0-速度:0.1-道路:A-11-公里:90.0-档位:1\n\
[2018-Mar-13 13:14:01.819966]-2.440 s=>驾驶员:0-速度:0.1-道路:A-11-公里:90.0-顺序:1\n\
[2018-Mar-13 13:14:01.819966]-2.440 s=>驾驶员:0-速度:0.2-道路:A-11-公里:90.0-离合器:1\n\
[2018-Mar-13 13:14:01.819966]-2.540 s=>注册表备份\n\
[2018-Mar-13 13:14:02.409855]-3.030 s=>驾驶员:0-速度:0.2-道路:A-11-公里:90.0-顺序:4\n\
[2018-Mar-13 13:14:02.409855]-3.030 s=>驾驶员:0-速度:0.3-道路:A-11-公里:90.0-顺序:8\n\
[2018-Mar-13 13:14:01.819966]-3.110 s=>注册表备份\n\
[2018-Mar-13 13:14:02.620424]-3.240 s=>驾驶员:0-速度:0.4-道路:A-11-公里:90.1-顺序:15\n\
[2018-Mar-13 13:14:02.829983]-3.450秒=>驾驶员:0-速度:0.6-道路:B-302-公里:90.1-坡度:-5\n\
[2018-Mar-13 13:14:03.039600]-3.660 s=>驾驶员:0-速度:0.8-道路:B-302-公里:90.1-顺序:21\n\
[2018-Mar-13 13:14:03.250451]-3.870 s=>驾驶员:0-速度:1.2-道路:B-302-公里:90.2-档位:2\n\
[2018-Mar-13 13:14:03.460012]-4.080 s=>驾驶员:0-速度:1.7-道路:B-302-公里:90.3-顺序:29\n\
[2018-Mar-13 13:14:03.669448]-4.290 s=>驾驶员:0-速度:2.2-道路:B-302-公里:90.4-顺序:34\n\
[2018-Mar-13 13:14:03.880066]-4.500秒=>驾驶员:0-速度:2.8-道路:B-302-公里:90.5-离合器:1\n\
[2018-Mar-13 13:14:04.090444]-4.710 s=>驾驶员:0-速度:3.5-道路:B-302-公里:90.7-顺序:45\n\
[2018-Mar-13 13:14:04.300160]-4.920 s=>驾驶员:0-速度:4.2-道路:B-302-公里:90.9-坡度:10\n\
[2018-Mar-13 13:14:04.510025]-5.130秒=>驾驶员:0-速度:4.9-道路:B-302-公里:91.1-档位:3”;
const auto len=std::distance(std::begin(输入),std::end(输入));
结构序列
{
int-ms;
int驱动程序;
int序列;
双倍时间;
双水平;
双公里;
std::字符串日期;
标准:弦道;;
};
命名空间xp=boost::xpressive;
int main()
{
序列数据;
std::向量序列;
使用命名空间xp;
cregex real=(+\u d>'.>>+\u d);
cregex关键字=“-SEQUENCE:”>(+_d)[xp::ref(data.SEQUENCE)=as(_)];
cregex date=repeat(_d)>>'-'>>repeat(alpha)>>'-'>>repeat(_d)>>>>repeat(_d)>>>':'>>repeat(_d)>>>':'>>repeat(_d);
cregex头文件='['>>日期[xp::ref(data.date)=\]>'.>>(+\[xp::ref(data.ms)=as(\]>>“]-”
>>real[xp::ref(data.time)=as(ux)]
>>“s=>Driver:”>>(+\ud)[xp::ref(data.Driver)=as(\ud)]
>>“-Speed:”>>real[xp::ref(data.vel)=as(u)]
>>“-Road:”>>(+set[alnum |'-'])[xp::ref(data.Road)=]
>>“-Km:”>>real[xp::ref(data.Km)=as(u3;)];
xp::cregex解析器=(标题>>关键字>>\u ln);
cregex_迭代器cur(输入,输入+len,解析器);
xp::cregex_迭代器端;
对于(;cur!=end;++cur)
序列。安置(数据);
返回0;
}

请注意VS 2010的限制。

我大致看到了两个需要改进的方面:

  • 您基本上解析所有行,包括您不感兴趣的行
  • 你分配了很多字符串
我建议使用字符串视图来修复分配。接下来,您可以尝试避免解析与序列模式不匹配的行。原则上没有理由不使用boostxpressive,但我选择的武器恰好是boostspirit,所以我也会包括它

有选择性 在花费更多精力之前,您可以检测到有趣的线条,如下所示:

cregex signature = -*~_n >> " - SEQUENCE: " >> (+_d) >> before(_ln|eos); 
for (xp::cregex_iterator cur(b, e, signature), end; cur != end; ++cur) {
    std::cout << "'" << cur->str() << "'\n";
}
template <typename It> struct QiParser : qi::grammar<It, Sequence()> {
    QiParser() : QiParser::base_type(line) {
        using namespace qi;
        auto date_time = copy(
            repeat(4)[digit] >> '-' >> repeat(3)[alpha] >> '-' >> repeat(2)[digit] >> ' ' >> 
            repeat(2)[digit] >> ':' >> repeat(2)[digit] >> ':' >> repeat(2)[digit] >> '.' >> +digit);

        line = '[' >> raw[date_time] >> "] - "
            >> double_ >> " s"
            >> " => Driver: "  >> int_
            >> " - Speed: "    >> double_
            >> " - Road: "     >> raw[+graph]
            >> " - Km: "       >> double_
            >> " - SEQUENCE: " >> int_
            >> (eol|eoi);
    }
  private:
    qi::rule<It, Sequence()> line;
};
没有分配任何资源。这应该很快

减少拨款 对于这一点,我将转向精神,因为它将使事情变得更容易

注意:我切换到这里的真正原因是,与Boost Spirit相比,Xpression似乎没有可扩展属性传播特性。这可能是我缺乏这方面的经验

另一种方法几乎肯定会用手动传播代码取代操作,而手动传播代码又会通知指定的捕获组,以保持内容的易读性。我不确定这些工具的性能开销,所以现在我们不要使用它们

您可以使用带有特征的
boost::string\u view
来“教导”Qi为其分配文本:

namespace boost { namespace spirit { namespace traits {
    template <typename It>
    struct assign_to_attribute_from_iterators<boost::string_view, It, void> {
        static inline void call(It f, It l, boost::string_view& attr) { attr = boost::string_view { &*f, size_t(std::distance(f,l)) }; }
    };
} } }
完整基准代码 基准用于测量和统计分析

  • 完整的交互式图表如下:
  • 使用
    -DUSE_NONIUS
    编译
  • 使用
    -DVERI编译
    
    namespace boost { namespace spirit { namespace traits {
        template <typename It>
        struct assign_to_attribute_from_iterators<boost::string_view, It, void> {
            static inline void call(It f, It l, boost::string_view& attr) { attr = boost::string_view { &*f, size_t(std::distance(f,l)) }; }
        };
    } } }
    
    template <typename It> struct QiParser : qi::grammar<It, Sequence()> {
        QiParser() : QiParser::base_type(line) {
            using namespace qi;
            auto date_time = copy(
                repeat(4)[digit] >> '-' >> repeat(3)[alpha] >> '-' >> repeat(2)[digit] >> ' ' >> 
                repeat(2)[digit] >> ':' >> repeat(2)[digit] >> ':' >> repeat(2)[digit] >> '.' >> +digit);
    
            line = '[' >> raw[date_time] >> "] - "
                >> double_ >> " s"
                >> " => Driver: "  >> int_
                >> " - Speed: "    >> double_
                >> " - Road: "     >> raw[+graph]
                >> " - Km: "       >> double_
                >> " - SEQUENCE: " >> int_
                >> (eol|eoi);
        }
      private:
        qi::rule<It, Sequence()> line;
    };
    
    #include <boost/fusion/adapted/struct.hpp>
    #include <boost/spirit/include/qi.hpp>
    #include <boost/spirit/repository/include/qi_seek.hpp>
    #include <boost/utility/string_view.hpp>
    #include <cstring> // strlen
    
    using It = char const*;
    
    struct Sequence {
        int driver;
        int sequence;
        double time;
        double vel;
        double km;
        boost::string_view date;
        boost::string_view road;
    };
    
    BOOST_FUSION_ADAPT_STRUCT(::Sequence, date, time, driver, vel, road, km, sequence)
    
    namespace qi = boost::spirit::qi;
    
    namespace boost { namespace spirit { namespace traits {
        template <typename It>
        struct assign_to_attribute_from_iterators<boost::string_view, It, void> {
            static inline void call(It f, It l, boost::string_view& attr) { attr = boost::string_view { &*f, size_t(std::distance(f,l)) }; }
        };
    } } }
    
    std::vector<Sequence> parse_spirit(It b, It e) {
    
        qi::rule<It, Sequence()> static const line = []{
            using namespace qi;
            auto date_time = copy(
                repeat(4)[digit] >> '-' >> repeat(3)[alpha] >> '-' >> repeat(2)[digit] >> ' ' >> 
                repeat(2)[digit] >> ':' >> repeat(2)[digit] >> ':' >> repeat(2)[digit] >> '.' >> +digit);
    
            qi::rule<It, Sequence()> r = '[' >> raw[date_time] >> "] - "
                >> double_ >> " s"
                >> " => Driver: "  >> int_
                >> " - Speed: "    >> double_
                >> " - Road: "     >> raw[+graph]
                >> " - Km: "       >> double_
                >> " - SEQUENCE: " >> int_
                >> (eol|eoi);
    
            return r;
        }();
    
        std::vector<Sequence> sequences;
    
        parse(b, e, *boost::spirit::repository::qi::seek[line], sequences);
    
        return sequences;
    }
    
    static char input[] = /*... see question ...*/;
    static const size_t len = strlen(input);
    
    int main() {
        auto sequences = parse_spirit(input, input+len);
        std::cout << "Parsed: " << sequences.size() << " sequence lines\n";
    }
    
    #include <boost/fusion/adapted/struct.hpp>
    #include <boost/spirit/include/qi.hpp>
    #include <boost/spirit/repository/include/qi_seek.hpp>
    #include <boost/utility/string_view.hpp>
    #include <cstring> // strlen
    
    using It = char const*;
    
    struct BaseEvent {
        int driver;
        int sequence;
        double time;
        double vel;
        double km;
        boost::string_view date;
        boost::string_view road;
    };
    struct Sequence : BaseEvent{};
    struct Clutch : BaseEvent{};
    struct Gear : BaseEvent{};
    
    BOOST_FUSION_ADAPT_STRUCT(::Sequence, date, time, driver, vel, road, km, sequence)
    BOOST_FUSION_ADAPT_STRUCT(::Clutch, date, time, driver, vel, road, km, sequence)
    BOOST_FUSION_ADAPT_STRUCT(::Gear, date, time, driver, vel, road, km, sequence)
    
    struct LogEvents {
        std::vector<Sequence> sequence;
        std::vector<Clutch> clutch;
        std::vector<Gear> gear;
    
        void add(Sequence const& s) { sequence.push_back(s); }
        void add(Clutch   const& c) { clutch.push_back(c);   }
        void add(Gear     const& g) { gear.push_back(g);     }
    };
    
    namespace qi = boost::spirit::qi;
    
    namespace boost { namespace spirit { namespace traits {
        template <typename It>
        struct assign_to_attribute_from_iterators<boost::string_view, It, void> {
            static inline void call(It f, It l, boost::string_view& attr) { attr = boost::string_view { &*f, size_t(std::distance(f,l)) }; }
        };
    
        template <> struct is_container<LogEvents> : std::true_type {};
    
        template <> struct container_value<LogEvents> {
            using type = boost::variant<::Sequence, ::Clutch, ::Gear>;
        };
    
        template <typename T> struct push_back_container<LogEvents, T> {
            struct Visitor {
                LogEvents& _log;
                template <typename U> void operator()(U const& ev) const { _log.add(ev); }
                using result_type = void;
            };
    
            template <typename... U>
            static bool call(LogEvents& log, boost::variant<U...> const& attribute) {
                boost::apply_visitor(Visitor{log}, attribute);
                return true;
            }
        };
    } } }
    
    
    namespace QiParsers {
        template <typename It, typename Attribute>
        struct BaseEventParser : qi::grammar<It, Attribute()> {
            BaseEventParser(std::string const& event_type) : BaseEventParser::base_type(start) {
                using namespace qi;
                auto date_time = copy(
                        repeat(4)[digit] >> '-' >> repeat(3)[alpha] >> '-' >> repeat(2)[digit] >> ' ' >> 
                        repeat(2)[digit] >> ':' >> repeat(2)[digit] >> ':' >> repeat(2)[digit] >> '.' >> +digit);
    
                start 
                    = '[' >> raw[date_time] >> "] - "
                    >> double_ >> " s"
                    >> " => Driver: "  >> int_
                    >> " - Speed: "    >> double_
                    >> " - Road: "     >> raw[+graph]
                    >> " - Km: "       >> double_
                    >> " - " >> lit(event_type) >> ": " >> int_
                    >> (eol|eoi);
            }
    
          private:
            qi::rule<It, Attribute()> start;
        };
    }
    
    LogEvents parse_spirit(It b, It e) {
        QiParsers::BaseEventParser<It, ::Sequence> sequence("SEQUENCE");
        QiParsers::BaseEventParser<It, ::Clutch>   clutch("CLUTCH");
        QiParsers::BaseEventParser<It, ::Gear>     gear("GEAR");
    
        LogEvents events;
        assert(parse(b, e, *boost::spirit::repository::qi::seek[sequence|clutch|gear], events));
    
        return events;
    }
    
    static char input[] = /* see question */;
    static const size_t len = strlen(input);
    
    int main() {
        auto events = parse_spirit(input, input+len);
        std::cout << "Events: "
            << events.sequence.size() << " sequence, "
            << events.clutch.size() << " clutch, "
            << events.gear.size() << " gear events\n";
    
        using boost::fusion::operator<<;
        for (auto& s : events.sequence) { std::cout << "SEQUENCE: " <<  s << "\n"; }
        for (auto& c : events.clutch)   { std::cout << "CLUTCH:   " <<  c << "\n"; }
        for (auto& g : events.gear)     { std::cout << "GEAR:     " <<  g << "\n"; }
    }
    
    #include <boost/fusion/adapted/struct.hpp>
    #include <boost/spirit/include/qi.hpp>
    #include <boost/spirit/repository/include/qi_seek.hpp>
    #include <boost/utility/string_view.hpp>
    #include <cstring> // strlen
    
    using It = char const*;
    
    namespace MyEvents {
        struct BaseEvent {
            int driver;
            int sequence;
            double time;
            double vel;
            double km;
            boost::string_view date;
            boost::string_view road;
        };
        struct Sequence : BaseEvent{};
        struct Clutch : BaseEvent{};
        struct Gear : BaseEvent{};
    
        using LogEvent = boost::variant<Sequence, Clutch, Gear>;
        using LogEvents = std::vector<LogEvent>;
    }
    
    BOOST_FUSION_ADAPT_STRUCT(MyEvents::Sequence, date, time, driver, vel, road, km, sequence)
    BOOST_FUSION_ADAPT_STRUCT(MyEvents::Clutch,   date, time, driver, vel, road, km, sequence)
    BOOST_FUSION_ADAPT_STRUCT(MyEvents::Gear,     date, time, driver, vel, road, km, sequence)
    
    namespace qi = boost::spirit::qi;
    
    namespace boost { namespace spirit { namespace traits {
        template <typename It>
        struct assign_to_attribute_from_iterators<boost::string_view, It, void> {
            static inline void call(It f, It l, boost::string_view& attr) { attr = boost::string_view { &*f, size_t(std::distance(f,l)) }; }
        };
    } } }
    
    namespace QiParsers {
        template <typename It, typename Attribute>
        struct BaseEventParser : qi::grammar<It, Attribute()> {
            BaseEventParser(std::string const& event_type) : BaseEventParser::base_type(start) {
                using namespace qi;
                auto date_time = copy(
                        repeat(4)[digit] >> '-' >> repeat(3)[alpha] >> '-' >> repeat(2)[digit] >> ' ' >> 
                        repeat(2)[digit] >> ':' >> repeat(2)[digit] >> ':' >> repeat(2)[digit] >> '.' >> +digit);
    
                start 
                    = '[' >> raw[date_time] >> "] - "
                    >> double_ >> " s"
                    >> " => Driver: "  >> int_
                    >> " - Speed: "    >> double_
                    >> " - Road: "     >> raw[+graph]
                    >> " - Km: "       >> double_
                    >> " - " >> lit(event_type) >> ": " >> int_
                    >> (eol|eoi);
            }
    
          private:
            qi::rule<It, Attribute()> start;
        };
    
        template <typename It>
        struct LogParser : qi::grammar<It, MyEvents::LogEvents()> {
            LogParser() : LogParser::base_type(start) {
                using namespace qi;
                using boost::spirit::repository::qi::seek;
    
                event = sequence | clutch | gear ; // TODO add types
                start = *seek[event];
            }
    
          private:
            qi::rule<It, MyEvents::LogEvents()> start;
            qi::rule<It, MyEvents::LogEvent()> event;
            BaseEventParser<It, MyEvents::Sequence> sequence{"SEQUENCE"};
            BaseEventParser<It, MyEvents::Clutch>   clutch{"CLUTCH"};
            BaseEventParser<It, MyEvents::Gear>     gear{"GEAR"};
        };
    }
    
    MyEvents::LogEvents parse_spirit(It b, It e) {
        static QiParsers::LogParser<It> const parser {};
    
        MyEvents::LogEvents events;
        parse(b, e, parser, events);
    
        return events;
    }
    
    static char input[] = /* see question */;
    static const size_t len = strlen(input);
    
    namespace MyEvents { // for debug/demo
        using boost::fusion::operator<<;
        static inline char const* kind(Sequence const&) { return "SEQUENCE"; }
        static inline char const* kind(Clutch   const&) { return "CLUTCH"; }
        static inline char const* kind(Gear     const&) { return "GEAR"; }
    
        struct KindVisitor : boost::static_visitor<char const*> {
            template <typename T> char const* operator()(T const& ev) const { return kind(ev); }
        };
        static inline char const* kind(LogEvent const& ev) {
            return boost::apply_visitor(KindVisitor{}, ev);
        }
    }
    
    int main() {
        auto events = parse_spirit(input, input+len);
        std::cout << "Parsed: " << events.size() << " events\n";
    
        for (auto& e : events)
            std::cout << kind(e) << ": " << e << "\n"; 
    }
    
    #include <boost/fusion/adapted/struct.hpp>
    #include <boost/spirit/include/qi.hpp>
    #include <boost/spirit/repository/include/qi_seek.hpp>
    #include <boost/utility/string_view.hpp>
    #include <cstring> // strlen
    
    using It = char const*;
    
    namespace MyEvents {
        enum class Kind { Sequence, Clutch, Gear, Slope, Other };
    
        struct CommonFields {
            boost::string_view date;
            double duration;
        };
    
        struct BaseEvent {
            CommonFields common;
            int driver;
            int event_id;
            double vel;
            double km;
            boost::string_view road;
            Kind kind;
        };
    
        struct OtherEvent {
            CommonFields common;
            std::string message;
        };
    
        using LogEvent = boost::variant<BaseEvent, OtherEvent>;
        using LogEvents = std::vector<LogEvent>;
    }
    
    BOOST_FUSION_ADAPT_STRUCT(MyEvents::CommonFields, date, duration)
    BOOST_FUSION_ADAPT_STRUCT(MyEvents::BaseEvent, common, driver, vel, road, km, kind, event_id)
    BOOST_FUSION_ADAPT_STRUCT(MyEvents::OtherEvent, common, message)
    
    namespace qi = boost::spirit::qi;
    
    namespace boost { namespace spirit { namespace traits {
        template <typename It>
        struct assign_to_attribute_from_iterators<boost::string_view, It, void> {
            static inline void call(It f, It l, boost::string_view& attr) { attr = boost::string_view { &*f, size_t(std::distance(f,l)) }; }
        };
    } } }
    
    namespace QiParsers {
        template <typename It>
        struct LogParser : qi::grammar<It, MyEvents::LogEvents()> {
            using Kind = MyEvents::Kind;
    
            LogParser() : LogParser::base_type(start) {
                using namespace qi;
    
                kind.add
                    ("SEQUENCE", Kind::Sequence)
                    ("CLUTCH", Kind::Clutch)
                    ("GEAR", Kind::Gear)
                    ("SLOPE", Kind::Slope)
                    ;
    
                common_fields
                    = '[' >> raw[
                            repeat(4)[digit] >> '-' >> repeat(3)[alpha] >> '-' >> repeat(2)[digit] >> ' ' >> 
                            repeat(2)[digit] >> ':' >> repeat(2)[digit] >> ':' >> repeat(2)[digit] >> '.' >> +digit
                    ] >> "]"
                    >> " - " >> double_ >> " s";
    
                base_event
                    = common_fields
                    >> " => Driver: "  >> int_
                    >> " - Speed: "    >> double_
                    >> " - Road: "     >> raw[+graph]
                    >> " - Km: "       >> double_
                    >> " - " >> kind >> ": " >> int_;
    
                other_event
                    = common_fields
                    >> " => " >> *~char_("\r\n");
    
                event 
                    = (base_event | other_event) 
                    >> (eol|eoi);
    
                start = *boost::spirit::repository::qi::seek[event];
            }
    
          private:
            qi::rule<It, MyEvents::LogEvents()> start;
            qi::rule<It, MyEvents::LogEvent()> event;
    
            qi::rule<It, MyEvents::CommonFields()> common_fields;
            qi::rule<It, MyEvents::BaseEvent()> base_event;
            qi::rule<It, MyEvents::OtherEvent()> other_event;
    
            qi::symbols<char, MyEvents::Kind> kind;
        };
    }
    
    MyEvents::LogEvents parse_spirit(It b, It e) {
        static QiParsers::LogParser<It> const parser {};
    
        MyEvents::LogEvents events;
        parse(b, e, parser, events);
    
        return events;
    }
    
    static char input[] = /* see question */;
    static const size_t len = strlen(input);
    
    namespace MyEvents { // for debug/demo
        using boost::fusion::operator<<;
    
        static inline Kind getKind(BaseEvent const& be) { return be.kind; }
        static inline Kind getKind(OtherEvent const&) { return Kind::Other; }
    
        struct KindVisitor : boost::static_visitor<Kind> {
            template <typename T> Kind operator()(T const& ev) const { return getKind(ev); }
        };
        static inline Kind getKind(LogEvent const& ev) {
            return boost::apply_visitor(KindVisitor{}, ev);
        }
    
        static inline std::ostream& operator<<(std::ostream& os, Kind k) {
            switch(k) {
                case Kind::Sequence: return os << "SEQUENCE";
                case Kind::Clutch:   return os << "CLUTCH";
                case Kind::Gear:     return os << "GEAR";
                case Kind::Slope:    return os << "SLOPE";
                case Kind::Other:    return os << "(Other)";
            }
            return os;
        }
    }
    
    int main() {
        auto events = parse_spirit(input, input+len);
        std::cout << "Parsed: " << events.size() << " events\n";
    
        for (auto& e : events)
            std::cout << getKind(e) << ": " << e << "\n"; 
    }
    
    Parsed: 37 events
    SLOPE: ((2018-Mar-13 13:13:59.580482 0.2) 0 0 A-11 90 SLOPE 0)
    GEAR: ((2018-Mar-13 13:14:01.170203 1.79) 0 0 A-11 90 GEAR 0)
    GEAR: ((2018-Mar-13 13:14:01.170203 1.79) 0 0.1 A-11 90 GEAR 1)
    SEQUENCE: ((2018-Mar-13 13:14:01.819966 2.44) 0 0.1 A-11 90 SEQUENCE 1)
    CLUTCH: ((2018-Mar-13 13:14:01.819966 2.44) 0 0.2 A-11 90 CLUTCH 1)
    (Other): ((2018-Mar-13 13:14:01.819966 2.54) Backup to regestry)
    [...]
    
    #include <boost/multi_index_container.hpp>
    #include <boost/multi_index/ordered_index.hpp>
    #include <boost/multi_index/composite_key.hpp>
    #include <boost/multi_index/global_fun.hpp>
    
    namespace Indexing {
        namespace bmi = boost::multi_index;
    
        using MyEvents::LogEvent;
    
        double getDuration(LogEvent const& ev) { return getCommon(ev).duration; }
    
        using Table = bmi::multi_index_container<
            std::reference_wrapper<LogEvent const>, //LogEvent,
            bmi::indexed_by<
                bmi::ordered_non_unique<
                    bmi::tag<struct primary>,
                    bmi::composite_key<
                        LogEvent,
                        bmi::global_fun<LogEvent const&, MyEvents::Kind, MyEvents::getKind>,
                        bmi::global_fun<LogEvent const&, int,            MyEvents::getEventId>
                    >
                >,
                bmi::ordered_non_unique<
                    bmi::tag<struct duration>,
                    bmi::global_fun<LogEvent const&, double, getDuration>
                >
            >
        >;
    }
    
    Indexing::Table idx(events.begin(), events.end());
    
    /*
     * // To print all events, grouped by by kind and event id:
     * for (MyEvents::LogEvent const& e : idx)
     *     std::cout << getKind(e) << ": " << e << "\n"; 
     *
     * // Ordered by duration:
     * for (MyEvents::LogEvent const& e : idx.get<Indexing::duration>())
     *     std::cout << getKind(e) << ": " << e << "\n"; 
     */
    
    std::cout << "\nAll GEAR events ordered by event id:\n";
    for (MyEvents::LogEvent const& e : make_iterator_range(idx.equal_range(make_tuple(Kind::Gear))))
        std::cout << getKind(e) << ": " << e << "\n"; 
    
    std::cout << "\nOnly the SLOPE events with id 10:\n";
    for (MyEvents::LogEvent const& e : make_iterator_range(idx.equal_range(make_tuple(Kind::Slope, 10))))
        std::cout << getKind(e) << ": " << e << "\n"; 
    
    std::cout << "\nEvents with durations in [2s..3s):\n";
    auto& by_dur = idx.get<Indexing::duration>();
    
    for (MyEvents::LogEvent const& e : make_iterator_range(by_dur.lower_bound(2), by_dur.upper_bound(3)))
        std::cout << getKind(e) << ": " << e << "\n"; 
    
    #include <boost/fusion/adapted/struct.hpp>
    #include <boost/spirit/include/qi.hpp>
    #include <boost/spirit/repository/include/qi_seek.hpp>
    #include <boost/utility/string_view.hpp>
    #include <cstring> // strlen
    
    using It = char const*;
    
    namespace MyEvents {
        enum class Kind { Sequence, Clutch, Gear, Slope, Other };
    
        struct CommonFields {
            boost::string_view date;
            double duration;
        };
    
        struct BaseEvent {
            CommonFields common;
            int driver;
            int event_id;
            double vel;
            double km;
            boost::string_view road;
            Kind kind;
        };
    
        struct OtherEvent {
            CommonFields common;
            std::string message;
        };
    
        using LogEvent = boost::variant<BaseEvent, OtherEvent>;
        using LogEvents = std::vector<LogEvent>;
    }
    
    BOOST_FUSION_ADAPT_STRUCT(MyEvents::CommonFields, date, duration)
    BOOST_FUSION_ADAPT_STRUCT(MyEvents::BaseEvent, common, driver, vel, road, km, kind, event_id)
    BOOST_FUSION_ADAPT_STRUCT(MyEvents::OtherEvent, common, message)
    
    namespace qi = boost::spirit::qi;
    
    namespace boost { namespace spirit { namespace traits {
        template <typename It>
        struct assign_to_attribute_from_iterators<boost::string_view, It, void> {
            static inline void call(It f, It l, boost::string_view& attr) { attr = boost::string_view { &*f, size_t(std::distance(f,l)) }; }
        };
    } } }
    
    namespace QiParsers {
        template <typename It>
        struct LogParser : qi::grammar<It, MyEvents::LogEvents()> {
            using Kind = MyEvents::Kind;
    
            LogParser() : LogParser::base_type(start) {
                using namespace qi;
    
                kind.add
                    ("SEQUENCE", Kind::Sequence)
                    ("CLUTCH", Kind::Clutch)
                    ("GEAR", Kind::Gear)
                    ("SLOPE", Kind::Slope)
                    ;
    
                common_fields
                    = '[' >> raw[
                            repeat(4)[digit] >> '-' >> repeat(3)[alpha] >> '-' >> repeat(2)[digit] >> ' ' >> 
                            repeat(2)[digit] >> ':' >> repeat(2)[digit] >> ':' >> repeat(2)[digit] >> '.' >> +digit
                    ] >> "]"
                    >> " - " >> double_ >> " s";
    
                base_event
                    = common_fields
                    >> " => Driver: "  >> int_
                    >> " - Speed: "    >> double_
                    >> " - Road: "     >> raw[+graph]
                    >> " - Km: "       >> double_
                    >> " - " >> kind >> ": " >> int_;
    
                other_event
                    = common_fields
                    >> " => " >> *~char_("\r\n");
    
                event 
                    = (base_event | other_event) 
                    >> (eol|eoi);
    
                start = *boost::spirit::repository::qi::seek[event];
            }
    
          private:
            qi::rule<It, MyEvents::LogEvents()> start;
            qi::rule<It, MyEvents::LogEvent()> event;
    
            qi::rule<It, MyEvents::CommonFields()> common_fields;
            qi::rule<It, MyEvents::BaseEvent()> base_event;
            qi::rule<It, MyEvents::OtherEvent()> other_event;
    
            qi::symbols<char, MyEvents::Kind> kind;
        };
    }
    
    MyEvents::LogEvents parse_spirit(It b, It e) {
        static QiParsers::LogParser<It> const parser {};
    
        MyEvents::LogEvents events;
        parse(b, e, parser, events);
    
        return events;
    }
    
    static char input[] = /* see question */;
    static const size_t len = strlen(input);
    
    namespace MyEvents { // for debug/demo
        using boost::fusion::operator<<;
    
        static inline CommonFields const& getCommon(BaseEvent const& be) { return be.common; }
        static inline CommonFields const& getCommon(OtherEvent const& oe) { return oe.common; }
        static inline Kind getKind(BaseEvent const& be) { return be.kind; }
        static inline Kind getKind(OtherEvent const&) { return Kind::Other; }
        static inline int getEventId(BaseEvent const& be) { return be.event_id; }
        static inline int getEventId(OtherEvent const&) { return 0; }
    
    #define IMPL_DISPATCH(name, T)                                                                     \
        struct name##Visitor : boost::static_visitor<T> {                                              \
            template <typename E> T operator()(E const &ev) const { return name(ev); }                 \
        };                                                                                             \
        static inline T name(LogEvent const &ev) { return boost::apply_visitor(name##Visitor{}, ev); }
    
        IMPL_DISPATCH(getCommon, CommonFields const&)
        IMPL_DISPATCH(getKind, Kind)
        IMPL_DISPATCH(getEventId, int)
    
        static inline std::ostream& operator<<(std::ostream& os, Kind k) {
            switch(k) {
                case Kind::Sequence: return os << "SEQUENCE";
                case Kind::Clutch:   return os << "CLUTCH";
                case Kind::Gear:     return os << "GEAR";
                case Kind::Slope:    return os << "SLOPE";
                case Kind::Other:    return os << "(Other)";
            }
            return os;
        }
    }
    
    #include <boost/multi_index_container.hpp>
    #include <boost/multi_index/ordered_index.hpp>
    #include <boost/multi_index/composite_key.hpp>
    #include <boost/multi_index/global_fun.hpp>
    
    namespace Indexing {
        namespace bmi = boost::multi_index;
    
        using MyEvents::LogEvent;
    
        double getDuration(LogEvent const& ev) { return getCommon(ev).duration; }
    
        using Table = bmi::multi_index_container<
            std::reference_wrapper<LogEvent const>, //LogEvent,
            bmi::indexed_by<
                bmi::ordered_non_unique<
                    bmi::tag<struct primary>,
                    bmi::composite_key<
                        LogEvent,
                        bmi::global_fun<LogEvent const&, MyEvents::Kind, MyEvents::getKind>,
                        bmi::global_fun<LogEvent const&, int,            MyEvents::getEventId>
                    >
                >,
                bmi::ordered_non_unique<
                    bmi::tag<struct duration>,
                    bmi::global_fun<LogEvent const&, double, getDuration>
                >
            >
        >;
    }
    
    using boost::make_iterator_range;
    using boost::make_tuple;
    
    int main() {
        using MyEvents::LogEvent;
        using MyEvents::Kind;
    
        auto events = parse_spirit(input, input+len);
        std::cout << "Parsed: " << events.size() << " events\n";
    
        Indexing::Table idx(events.begin(), events.end());
    
        /*
         * // To print all events, grouped by by kind and event id:
         * for (MyEvents::LogEvent const& e : idx)
         *     std::cout << getKind(e) << ": " << e << "\n"; 
         *
         * // Ordered by duration:
         * for (MyEvents::LogEvent const& e : idx.get<Indexing::duration>())
         *     std::cout << getKind(e) << ": " << e << "\n"; 
         */
    
        std::cout << "\nAll GEAR events ordered by event id:\n";
        for (MyEvents::LogEvent const& e : make_iterator_range(idx.equal_range(make_tuple(Kind::Gear))))
            std::cout << getKind(e) << ": " << e << "\n"; 
    
        std::cout << "\nOnly the SLOPE events with id 10:\n";
        for (MyEvents::LogEvent const& e : make_iterator_range(idx.equal_range(make_tuple(Kind::Slope, 10))))
            std::cout << getKind(e) << ": " << e << "\n"; 
    
        std::cout << "\nEvents with durations in [2s..3s):\n";
        auto& by_dur = idx.get<Indexing::duration>();
    
        for (MyEvents::LogEvent const& e : make_iterator_range(by_dur.lower_bound(2), by_dur.upper_bound(3)))
            std::cout << getKind(e) << ": " << e << "\n"; 
    }
    
    Parsed: 37 events
    
    All GEAR events ordered by event id:
    GEAR: ((2018-Mar-13 13:14:01.170203 1.79) 0 0 A-11 90 GEAR 0)
    GEAR: ((2018-Mar-13 13:14:01.170203 1.79) 0 0 A-11 90 GEAR 0)
    GEAR: ((2018-Mar-13 13:14:01.170203 1.79) 0 0.1 A-11 90 GEAR 1)
    GEAR: ((2018-Mar-13 13:14:01.170203 1.79) 0 0.1 A-11 90 GEAR 1)
    GEAR: ((2018-Mar-13 13:14:03.250451 3.87) 0 1.2 B-302 90.2 GEAR 2)
    GEAR: ((2018-Mar-13 13:14:03.250451 3.87) 0 1.2 B-302 90.2 GEAR 2)
    GEAR: ((2018-Mar-13 13:14:04.510025 5.13) 0 4.9 B-302 91.1 GEAR 3)
    
    Only the SLOPE events with id 10:
    SLOPE: ((2018-Mar-13 13:14:04.300160 4.92) 0 4.2 B-302 90.9 SLOPE 10)
    SLOPE: ((2018-Mar-13 13:14:04.300160 4.92) 0 4.2 B-302 90.9 SLOPE 10)
    
    Events with durations in [2s..3s):
    SEQUENCE: ((2018-Mar-13 13:14:01.819966 2.44) 0 0.1 A-11 90 SEQUENCE 1)
    CLUTCH: ((2018-Mar-13 13:14:01.819966 2.44) 0 0.2 A-11 90 CLUTCH 1)
    SEQUENCE: ((2018-Mar-13 13:14:01.819966 2.44) 0 0.1 A-11 90 SEQUENCE 1)
    CLUTCH: ((2018-Mar-13 13:14:01.819966 2.44) 0 0.2 A-11 90 CLUTCH 1)
    (Other): ((2018-Mar-13 13:14:01.819966 2.54) Backup to regestry)
    (Other): ((2018-Mar-13 13:14:01.819966 2.54) Backup to regestry)