[英]Boost Spirit: slow parsing optimization
我是 Spirit 和 Boost 的新手。 我正在嘗試解析 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 秒,這對於我的需要來說非常慢。 我的問題是有沒有辦法以更優化的方式編寫解析規則以使其(更快)速度? 一般來說,我該如何改進這個實現?
我是 Spirit 的新手,因此非常感謝您的一些解釋。
我已將您的語法連接到Nonius基准測試中,並生成了約 85k 行的均勻隨機輸入數據(下載: http ://stackoverflow-sehe.s3.amazonaws.com/input.txt,7.4 MB)。
在預先讀取文件時,我始終有大約 36 毫秒的時間來解析整個文件。
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 精神船長問題。
#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);
});
})
圖形:
另一個運行輸出,實時:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.