使用提升精神分裂字符串

时间:2015-05-05 07:17:39

标签: c++ split boost-spirit boost-spirit-qi string-split

这是个好主意吗?出于某种原因,我认为它应该比boost的tokenizer或split更快。但是大部分时间我都陷入了boost :: spirit :: compile

template <typename Iterator>
struct ValueList : bsq::grammar<Iterator, std::vector<std::string>()>
{
    ValueList(const std::string& sep, bool isCaseSensitive) : ValueList::base_type(query)
    {
        if(isCaseSensitive)
        {
            query = value >> *(sep >> value);
            value = *(bsq::char_ - sep);
        }
        else
        {
            auto separator = bsq::no_case[sep];
            query = value >> *(separator >> value);
            value = *(bsq::char_ - separator);
        }
    }
    bsq::rule<Iterator, std::vector<std::string>()> query;
    bsq::rule<Iterator, std::string()> value;
};

inline bool Split(std::vector<std::string>& result, const std::string& buffer, const std::string& separator,
                  bool isCaseSensitive)
{
    result.clear();
    ValueList<std::string::const_iterator> parser(separator, isCaseSensitive);
    auto itBeg = buffer.begin();
    auto itEnd = buffer.end();
    if(!(bsq::parse(itBeg, itEnd, parser, result) && (itBeg == itEnd)))
        result.push_back(buffer);
    return true;
}

我已按照上面的说明实现了它。我的代码出了什么问题?或者只是因为在运行时定义了分隔符,重新编译是不可避免的?

EDIT001: 示例并与使用CoLiRu上的tokenizer的boost :: split和original imp的可能实现进行比较 看起来coliru现在已经失败了。在任何情况下,这些都是对字符串“2lkhj309 | ioperwkl | 20sdf39i | rjjdsf | klsdjf230o | kx23904iep2 | xp39f4p2 | xlmq2i3219”的1M运行结果,带分隔符“|”

  

8000000在1081ms内分裂。
8000000分裂在1169ms。
8000000   在2663ms分裂。

首先是tokenizer,第二个是boost :: split,第三个是boost :: spirit

1 个答案:

答案 0 :(得分:4)

首先,不同的版本不会做同样的事情:

  • 特别是令牌压缩的行为有所不同(我为input.getText()修复了它,但它似乎不是boost::split的功能)
  • 分隔符被视为字符串文字而不是精神版本中的字符集(已修复)

是的,使用动态分隔符重新编译是不可避免的。但不,这不是瓶颈(其他方法也有动态分隔符):

我做了一些优化。时间安排:

  • Coliru clang++

    boost::tokenizer
  • Coliru g++

    8000000 original (boost::tokenizer) rate: 2.84257μs
    10000000 possible (boost::split) rate: 3.09941μs
    10000000 spirit (dynamic version) rate: 1.45456μs
    10000000 spirit (direct version, avoid type erasure) rate: 1.25588μs
    
    next step:
    10000000 spirit (precompiled sep) rate: 1.18059μs
    
  • 本地系统g ++:

    8000000 original (boost::tokenizer) rate: 2.92805μs
    10000000 possible (boost::split) rate: 2.75442μs
    10000000 spirit (dynamic version) rate: 1.32821μs
    10000000 spirit (direct version, avoid type erasure) rate: 1.10712μs
    
    next step:
    10000000 spirit (precompiled sep) rate: 1.0791μs
    

正如你所看到的,灵魂方法不需要慢。我采取了哪些步骤? http://paste.ubuntu.com/11001344/

  • 重构基准显示率(无变化)3.523μs
  • 调用者的case_insensitivity选择(如果需要,只需使用sehe@desktop:/tmp$ time ./test 8000000 original (boost::tokenizer) rate: 1.80061μs 10000000 possible (boost::split) rate: 1.29754μs 10000000 spirit (dynamic version) rate: 0.607789μs 10000000 spirit (direct version, avoid type erasure) rate: 0.488087μs next step: 10000000 spirit (precompiled sep) rate: 0.498769μs 2.742μs。
  • 消除no_case[char_(delimiter)]子规则(由于类型擦除的非终端规则,减少了复制和动态调度)2.579μs。
  • 制作分隔符字符集而不是字符串文字:2.693μs。

      

    请参阅coliru上的intermediate version(我推荐下面的代码,它更清理了)

  • 使用qi :: raw []代替std :: string合成属性(避免复制!)0.624072μs

  • 消除所有非终端(即类型擦除;参见value实施)速率:0.491011μs

现在看来很明显,每次都不会“编译”分隔符,所有实现都会受益。我没有为所有的方法做到这一点,但为了好玩,让我们为Spirit版本做:

  • 使用硬编码'|'分隔符0.455269μs //固定分隔符

完整列表:

<强> Live On Coliru

spirit_direct