2015-10-06 95 views
2

我是新来的精神和一般的提升。我试图解析类似如下的VRML文件的一部分:提升精神:缓慢解析优化

point 
      [ 
      #coordinates written in meters. 
      -3.425386e-001 -1.681608e-001 0.000000e+000, 
      -3.425386e-001 -1.642545e-001 0.000000e+000, 
      -3.425386e-001 -1.603483e-001 0.000000e+000, 

注释与是可选的开始。

我写了一个语法,工作正常,但解析过程花了很长时间。我想优化它以更快地运行。我的代码如下所示:

struct Point 
{ 
    double a; 
    double b; 
    double c; 

    Point() : a(0.0), b(0.0), c(0.0){} 
}; 

BOOST_FUSION_ADAPT_STRUCT 
(
    Point, 
    (double, a) 
    (double, b) 
    (double, c) 
) 

namespace qi = boost::spirit::qi; 
namespace repo = boost::spirit::repository; 

template <typename Iterator> 
struct PointParser : 
public qi::grammar<Iterator, std::vector<Point>(), qi::space_type> 
{ 
    PointParser() : PointParser::base_type(start, "PointGrammar") 
    { 
     singlePoint = qi::double_>>qi::double_>>qi::double_>>*qi::lit(","); 
     comment = qi::lit("#")>>*(qi::char_("a-zA-Z.") - qi::eol); 
     prefix = repo::seek[qi::lexeme[qi::skip[qi::lit("point")>>qi::lit("[")>>*comment]]]; 
     start %= prefix>>qi::repeat[singlePoint];  

     //BOOST_SPIRIT_DEBUG_NODES((prefix)(comment)(singlePoint)(start)); 
    } 

    qi::rule<Iterator, Point(), qi::space_type>    singlePoint; 
    qi::rule<Iterator, qi::space_type>      comment; 
    qi::rule<Iterator, qi::space_type>      prefix; 
    qi::rule<Iterator, std::vector<Point>(), qi::space_type> start; 
}; 

,我打算解析部分,位于输入文本的中间,所以我需要跳过的文字部分,以得到它。我使用repo :: seek来实现它。这是最好的方法吗?

我以下列方式运行解析器:

std::vector<Point> points; 
typedef PointParser<std::string::const_iterator> pointParser; 
pointParser g2; 

auto start = ch::high_resolution_clock::now(); 
bool r = phrase_parse(Data.begin(), Data.end(), g2, qi::space, points); 
auto end = ch::high_resolution_clock::now(); 

auto duration = ch::duration_cast<boost::chrono::milliseconds>(end - start).count(); 

要在输入文本解析约80K项,大约需要2.5秒,这是我的需求相当缓慢。我的问题是有没有办法以更优化的方式编写解析规则以使其更快(更快)?我怎样才能改进这个实现呢?

我是新来的精神,所以一些解释将不胜感激。

+0

如果相同的代码36毫秒和2.5秒的类似系统(球场)和类似的输入运行时间之间获得,我的办法是使后者没有优化上。 – rubenvb

+0

对此不确定。我使用VS2013的预编译版本的boost 1.59库。你能建议任何优化标志吗? –

+0

Boost(.Spirit)非常模板化,预编译的库不会让你放慢速度......你为什么认为*你的代码编译得太慢了?精神是在你使用它的时候编译的,而不是在前面。你需要确保你的代码的定时发布版本。 – rubenvb

回答

2

我已将您的语法挂钩到Nonius基准测试中,并生成约85k行的统一随机输入数据(下载:http://stackoverflow-sehe.s3.amazonaws.com/input.txt,7.4 MB)。

  • 你在测量发布版本的时间吗?
  • 你使用慢速文件输入吗?

当读取文件的前期我一直得到〜36ms时间来解析一大堆。

clock resolution: mean is 17.616 ns (40960002 iterations) 

benchmarking sample 
collecting 100 samples, 1 iterations each, in estimated 3.82932 s 
mean: 36.0971 ms, lb 35.9127 ms, ub 36.4456 ms, ci 0.95 
std dev: 1252.71 μs, lb 762.716 μs, ub 2.003 ms, ci 0.95 
found 6 outliers among 100 samples (6%) 
variance is moderately inflated by outliers 

代码:见下文。


注:

  • 你似乎发生冲突上使用的船长,并寻求在一起。我建议你简化prefix

    comment  = '#' >> *(qi::char_ - qi::eol); 
    
    prefix  = repo::seek[ 
            qi::lit("point") >> '[' >> *comment 
           ]; 
    

    prefix将使用空间船长,而忽略任何匹配的属性(因为该规则声明的类型)。让comment隐含一个语义从规则声明下降船长:

    // implicit lexeme: 
        qi::rule<Iterator>      comment; 
    

    注意更多的背景信息,请参阅Boost spirit skipper issues

Live On Coliru

#include <boost/fusion/adapted/struct.hpp> 
#include <boost/spirit/include/qi.hpp> 
#include <boost/spirit/repository/include/qi_seek.hpp> 

namespace qi = boost::spirit::qi; 
namespace repo = boost::spirit::repository; 

struct Point { double a = 0, b = 0, c = 0; }; 

BOOST_FUSION_ADAPT_STRUCT(Point, a, b, c) 

template <typename Iterator> 
struct PointParser : public qi::grammar<Iterator, std::vector<Point>(), qi::space_type> 
{ 
    PointParser() : PointParser::base_type(start, "PointGrammar") 
    { 
     singlePoint = qi::double_ >> qi::double_ >> qi::double_ >> *qi::lit(','); 

     comment  = '#' >> *(qi::char_ - qi::eol); 

     prefix  = repo::seek[ 
      qi::lit("point") >> '[' >> *comment 
      ]; 

     //prefix  = repo::seek[qi::lexeme[qi::skip[qi::lit("point")>>qi::lit("[")>>*comment]]]; 

     start  %= prefix >> *singlePoint; 

     //BOOST_SPIRIT_DEBUG_NODES((prefix)(comment)(singlePoint)(start)); 
    } 

    private: 
    qi::rule<Iterator, Point(), qi::space_type>    singlePoint; 
    qi::rule<Iterator, std::vector<Point>(), qi::space_type> start; 
    qi::rule<Iterator, qi::space_type>      prefix; 
    // implicit lexeme: 
    qi::rule<Iterator> comment; 
}; 

#include <nonius/benchmark.h++> 
#include <nonius/main.h++> 
#include <boost/iostreams/device/mapped_file.hpp> 

static boost::iostreams::mapped_file_source src("input.txt"); 

NONIUS_BENCHMARK("sample", [](nonius::chronometer cm) { 
    std::vector<Point> points; 

    using It = char const*; 
    PointParser<It> g2; 

    cm.measure([&](int) { 
     It f = src.begin(), l = src.end(); 
     return phrase_parse(f, l, g2, qi::space, points); 
     bool ok = phrase_parse(f, l, g2, qi::space, points); 
     if (ok) 
      std::cout << "Parsed " << points.size() << " points\n"; 
     else 
      std::cout << "Parsed failed\n"; 

     if (f!=l) 
      std::cout << "Remaining unparsed input: '" << std::string(f,std::min(f+30, l)) << "'\n"; 

     assert(ok); 
    }); 
}) 

图:

enter image description here

另一个运行输出,住:

+0

我已经把实况流录制(减去前13分钟......)放在这里:https://www.livecoding.tv/video/another-spirit-grammar-benchmark-nonius/([experiment](http: //chat.stackoverflow.com/transcript/10?m=24182469#24182469)) – sehe

+0

非常感谢您的回复。我在** debug ** build中测量时间。我不确定你是什么意思“预先读取文件”。我正在使用** boost :: iostreams :: mapped_file_source **读取文件,并通过** std :: string **将数据传递给解析器。除了** singlePoint **之外,我已经剥夺了所有规则,并且我仍然获得了大约2.5秒的时间。 –

+0

我试图从_comment_规则中删除船长并按照您的建议简化规则,但解析器无法正常工作。我在这里做错了什么? –