3

次のスニペットがあります。

#include <iostream>
#include <sstream>
#include <chrono>

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/classic.hpp>

namespace qi = boost::spirit::qi;
namespace classic = boost::spirit::classic;

template<typename T>
void output_time(const T& end, const T& begin)
{
   std::cout << std::chrono::duration_cast<std::chrono::seconds>(
         end - begin).count() << std::endl;
}

template<typename Iter>
struct qi_grammar : public qi::grammar<Iter>
{
   qi_grammar():qi_grammar::base_type(rule_)
   {
      rule_ = *string_;
      string_ = qi::char_('"') >> *(qi::char_ - '"') >> qi::char_('"');
   }
   qi::rule<Iter> rule_;
   qi::rule<Iter> string_;
};

template<typename Iter>
struct classic_grammar : public classic::grammar<classic_grammar<Iter>>
{
   template<typename ScannerT>
   struct definition
   {
      definition(const classic_grammar&)
      {
         rule = *string_;
         string_ = classic::ch_p('"') >> *(classic::anychar_p - '"') >> classic::ch_p('"');
      }
      classic::rule<ScannerT> rule, string_;
      const classic::rule<ScannerT>& start() const { return rule; }
   };
};

template<typename Iter>
void parse(Iter first, Iter last, const qi_grammar<Iter>& prs)
{
   auto start = std::chrono::system_clock::now();
   for (int i = 0; i < 100; ++i)
   {
      Iter next = first;
      if (!qi::parse(next, last, prs) || next != last)
      {
         assert(false);
      }
   }
   auto finish = std::chrono::system_clock::now();
   output_time(finish, start);
}

template<typename Iter>
void parse_c(Iter first, Iter last, const classic_grammar<Iter>& prs)
{
   auto start = std::chrono::system_clock::now();
   for (int i = 0; i < 100; ++i)
   {
      auto info = classic::parse(first, last, prs);
      if (!info.hit) assert(false);
   }
   auto finish = std::chrono::system_clock::now();
   output_time(finish, start);
}

int main()
{
   qi_grammar<std::string::const_iterator> qi_lexeme;
   classic_grammar<std::string::const_iterator> classic_lexeme;
   std::stringstream ss;
   for (int i = 0; i < 1024 * 500; ++i)
   {
      ss << "\"name\"";
   }
   const std::string s = ss.str();
   std::cout << "Size: " << s.size() << std::endl;
   std::cout << "Qi" << std::endl;
   parse(s.begin(), s.end(), qi_lexeme);
   std::cout << "Classic" << std::endl;
   parse_c(s.begin(), s.end(), classic_lexeme);
}

結果は

forever@pterois:~/My_pro1/cpp_pro$ ./simple_j 
Size: 3072000
Qi
0
Classic
1

したがって、qiはクラシックよりも高速に解析します。しかし、string_ruleの属性をstd :: string()(ie qi::rule<Iter, std::string()> string_;)に変更すると、

forever@pterois:~/My_pro1/cpp_pro$ ./simple_j 
Size: 3072000
Qi
19
Classic
1

とても遅いです。私は何か間違ったことをしていますか?ありがとう。

コンパイラ:gcc4.6.3。ブースト-1.48.0。フラグ:-std = c ++0x-O2。LWSの結果は同じです。

char_ieのセマンティックアクションの使用

string_ = qi::char_('"') >> *(qi::char_[boost::bind(&some_f, _1)] - '"')
 >> qi::char_('"')[boost::bind(&some_clear_f, _1)];

パフォーマンスを向上させますが、存在する場合は別の解決策も探しています。

4

1 に答える 1

4

以前、SOで非常によく似た質問に答えたと思います。悲しいことに、私はそれを見つけることができません。

つまり、一致するたびに文字列を割り当てる(およびコピーする)のではなく、ソースデータにイテレータを使用する方がよい場合があります。

使用する場合

qi::rule<Iter, boost::iterator_range<Iter>()> string_;
string_ = qi::raw [ qi::char_('"') >> *(qi::char_ - '"') >> qi::char_('"') ];

私は(かなり(16倍)大きいデータセットで)得ました:

Size: 49152000
Qi
12
Classic
11

実際、ルール自体をに変更した後

  string_ = qi::raw [ qi::lit('"') >> *~qi::char_('"') >> '"' ];

私は得た

Size: 49152000
Qi
7
Classic
11

だから...それはかなりまともだと思います。LWSでご覧ください:http://liveworkspace.org/code/opA5s$0

完全を期すために、次のようなことを行うことで、iterator_rangeから文字列を取得できることは明らかです。

const std::string dummy("hello world");
auto r = boost::make_iterator_range(begin(dummy), end(dummy));
std::string asstring(r.begin(), r.end());

秘訣は、実際の文字列の構築を必要なときに遅らせることです。このトリックを自動的に実行させたい場合があります。これはSpirit Lex、トークン属性に対して行うことです。あなたはそれを調べたいと思うかもしれません。

于 2013-01-19T16:15:01.730 に答える