【问题标题】:boost::spirit::qi performanceboost::spirit::qi 性能
【发布时间】:2013-01-18 11:56:20
【问题描述】:

我有以下 sn-p。

#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_ 规则的属性更改为 std::string() (即qi::rule&lt;Iter, std::string()&gt; string_;)时,我有

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

它非常非常慢。我做错了什么?谢谢。

编译器:gcc 4.6.3。提升 - 1.48.0。标志:-std=c++0x -O2。在 LWS 上的结果是一样的。

使用 char_ 的语义动作,即

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

提高性能,但我也在寻找另一种解决方案,如果存在的话。

【问题讨论】:

  • 什么编译器?什么编译器标志(优化等)?
  • @IgorR。 GCC 4.6.3。 -std=c++0x 和 -O2
  • 好像你的问题和这个类似:stackoverflow.com/questions/13343874/boost-spirit-qi-slow
  • @IgorR。嗯... mb,但它的问题的答案对我没有帮助。因为我已经在使用 -O2 + 我正在尝试使用 qi::repeat - 结果是一样的......处理字符串作为字符不是我的解决方案。
  • @IgorR。嗯...我错了,我认为...存储此字符的语义操作看起来像是一种可能的解决方案。

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


【解决方案1】:

我想我之前在 SO 上回答过一个非常相似的问题。可惜没找到。

简而言之,您可能更喜欢在源数据中使用迭代器,而不是在每次匹配时分配(和复制)字符串。

使用时

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

我得到了(具有相当大 (16x) 的数据集):

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 对令牌属性所做的事情。你可能想调查一下。

【讨论】:

  • edited 进行了澄清和优化,节省了约 40% 的运行时间,使 Qi 版本大量 优越(比classic 快1/3)。 (请注意,我在具有 8Mb RAM 的 Q9550 上使用了 gcc 4.7.2、64bit、-O3 -march=native -ltcmalloc) - 另请参阅 other answers 以获取有关 libtcmalloc 的信息
  • 哦...我不知道原始指令。非常感谢您。我想,这将是我的问题的答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-14
  • 2013-02-12
  • 1970-01-01
相关资源
最近更新 更多