2016-05-14 18 views
7

Tôi đang cố gắng nắm bắt được Spirit X3 mới (tăng 1.61.0).Giờ biên dịch với tinh thần tăng cường x3

Máy của tôi là MacBook Pro (i7-4750HQ) đang chạy Linux.

Có sử dụng phiên bản 2 của Thần tôi đã được sử dụng để biên dịch lần lớn, nhưng điều này không cảm thấy đúng. Đối với các bước đầu tiên sau của trình phân tích biểu thức, trình biên dịch cần 20 giây.

Tôi nghĩ X3 sẽ nhanh hơn, vậy điều này có hợp lý không? Mã của tôi có tối ưu không?

thiết lập trình biên dịch (kêu vang 3.8.0)

clang++ -c -pipe -std=c++14 -ftemplate-depth=512 -g -w -Wall -Wno-unused-parameter -fPIC 

Code:

//#define BOOST_SPIRIT_X3_DEBUG 
#include <iostream> 

#include <boost/spirit/home/x3.hpp> 
#include <boost/spirit/home/x3/support/ast/variant.hpp> 
#include <boost/fusion/include/adapt_struct.hpp> 

#include <string> 
#include <vector> 

//-------------------------------------------------------------------------------------------------- 
namespace client { namespace ast 
{ 
    namespace fusion = boost::fusion; 
    namespace x3 = boost::spirit::x3; 

    struct number : x3::variant<int, double> { 
     using base_type::base_type; 
     using base_type::operator=; 
    }; 

    struct add_ast; 
    struct mult_ast; 
    struct block_ast; 
    struct function; 

    struct expr_ast : x3::variant< 
      number, 
      x3::forward_ast<function>, 
      x3::forward_ast<add_ast>, 
      x3::forward_ast<mult_ast>, 
      x3::forward_ast<block_ast> 
     > { 
     using base_type::base_type; 
     using base_type::operator=; 
    }; 

    struct add_ast { 
     expr_ast lhs; 
     bool  add; 
     expr_ast rhs; 
    }; 

    struct mult_ast { 
     expr_ast lhs; 
     bool  mult; 
     expr_ast rhs; 
    }; 

    struct block_ast { 
     expr_ast body; 
    }; 

    struct function { 
     std::string   name; 
     std::vector<expr_ast> params; 
    }; 
}} 

//-------------------------------------------------------------------------------------------------- 
BOOST_FUSION_ADAPT_STRUCT(client::ast::add_ast, 
    (client::ast::expr_ast, lhs), 
    (bool, add), 
    (client::ast::expr_ast, rhs) 
) 
BOOST_FUSION_ADAPT_STRUCT(client::ast::mult_ast, 
    (client::ast::expr_ast, lhs), 
    (bool, mult), 
    (client::ast::expr_ast, rhs) 
) 
BOOST_FUSION_ADAPT_STRUCT(client::ast::block_ast, 
    (client::ast::expr_ast, body) 
) 
BOOST_FUSION_ADAPT_STRUCT(client::ast::function, 
    (std::string, name), 
    (std::vector<client::ast::expr_ast>, params) 
) 

//-------------------------------------------------------------------------------------------------- 
namespace client { namespace parser 
{ 
    namespace x3 = boost::spirit::x3; 

    const x3::rule<class expr,  ast::expr_ast> expr  = "expr"; 
    const x3::rule<class add_expr, ast::expr_ast> add_expr = "add_expr"; 
    const x3::rule<class mult_expr, ast::expr_ast> mult_expr = "mult_expr"; 
    const x3::rule<class block_expr, ast::expr_ast> block_expr = "block_expr"; 

    auto const number = x3::rule<class number, ast::number> {"number"} 
         = (x3::int_ >> !x3::lit('.')) | x3::double_; 

    auto const fct_name = x3::rule<class fct_name, std::string> {"fct_name"} 
         = x3::lexeme[ *x3::alpha >> *(x3::alnum | x3::char_('_')) ]; 

    auto const function = x3::rule<class function, ast::function> {"function"} 
         = fct_name >> x3::lit("(") >> -expr % ',' >> ")"; 

    auto const simple_expr = x3::rule<class simple_expr, ast::expr_ast> {"simple_expr"} 
          = function | number; 

    auto const block_term = x3::rule<class block_term, ast::block_ast> {"block_term"} 
          = "(" >> expr >> ")"; 

    auto const mult_term = x3::rule<class mult_term, ast::mult_ast> {"mult_term"} 
         = block_expr 
          >> ((x3::lit("*") >> x3::attr(true)) | (x3::lit("/") >> x3::attr(false))) 
          >> mult_expr; 

    auto const add_term = x3::rule<class add_term, ast::add_ast> {"add_term"} 
         = mult_expr 
          >> ((x3::lit("+") >> x3::attr(true)) | (x3::lit("-") >> x3::attr(false))) 
          >> add_expr; 

    auto const block_expr_def = block_term | simple_expr; 
    auto const mult_expr_def = mult_term | block_expr; 
    auto const add_expr_def = add_term | mult_expr; 
    auto const expr_def  = add_expr; 

    BOOST_SPIRIT_DEFINE(expr, add_expr, mult_expr, block_expr); 
}} 

//-------------------------------------------------------------------------------------------------- 
namespace client { namespace ast 
{ 
    struct printer 
    { 
     typedef std::string result_type; 

     std::string operator()(const expr_ast &ast) const 
     { 
      return boost::apply_visitor(printer(), ast); 
     } 
     std::string operator()(const number &value) const 
     { 
      return boost::apply_visitor(printer(), value); 
     } 

     std::string operator()(const add_ast &expr) const { 
      return "(" + boost::apply_visitor(printer(), expr.lhs) + (expr.add?" + ":" - ") 
        + boost::apply_visitor(printer(), expr.rhs) + ")"; 
     } 

     std::string operator()(const mult_ast &expr) const { 
      return "(" + boost::apply_visitor(printer(), expr.lhs) + (expr.mult?" * ":"/") 
        + boost::apply_visitor(printer(), expr.rhs) + ")"; 
     } 

     std::string operator()(const block_ast &expr) const { 
      return boost::apply_visitor(printer(), expr.body); 
     } 

     std::string operator()(const function &fct) const 
     { 
      std::string result = fct.name + "("; 
      for (std::size_t i = 0; i < fct.params.size(); ++i) { 
       result += printer()(fct.params[i]); 
       if (i != fct.params.size() - 1) 
        result += ","; 
      } 
      result += ")"; 
      return result; 
     } 

     std::string operator()(int const& value) const 
     { 
      return std::to_string(value); 
     } 
     std::string operator()(double const& value) const 
     { 
      return std::to_string(value); 
     } 
    }; 
}} 

//-------------------------------------------------------------------------------------------------- 
int main() 
{ 
    std::vector<std::string> storage = { 
     "foo()", "-foo()", 
     "f1_2()", 
     "foo_bar()", 
     "foo(bar (42, baz()))", 
     "foo(5)", "foo(-5)", 
     "foo(1.1, foo(4.21e-2, 4., 6))", 
     "1.1", "-1.1", 
     "1 * 1", 
     "foo(1 * 1) * bar(42)", 
     "foo(2 + 5.5, bar()*3.4-7)", 
     "foo(2 + 5.5, bar(baz(-5/foo())) * 3.4 - 7)", 
     "4 + 5 * 6", 
     "1+2+3+4*5*6*-7+-8*+9-0", 
     "(foo())", 
     "foo() * ((1+2)+3*(2+3))", 
     "(1+2)*3", "1+2*3", 
     "foo" 
    }; 

    using boost::spirit::x3::ascii::space; 

    for (const auto &item : storage) { 
     using client::parser::expr; // Our grammar 
     client::ast::expr_ast ast; // Our tree 

     std::string::const_iterator iter = item.begin(); 
     std::string::const_iterator end = item.end(); 
     bool r = phrase_parse(iter, end, expr, space, ast); 

     if (r && iter == end) 
     { 
      std::cout << "Ok: " << item << " result: " << client::ast::printer()(ast) << std::endl; 
     } 
     else 
     { 
      std::cout << "Fail: " << item << std::endl; 
     } 
    } 
} 
+0

15 giây cho tôi trên một macbook chuyên nghiệp hiện đại với -O0, 27 với -O3. Đã phải thêm 2 #includes để làm cho nó biên dịch - stdexcept và ngoại lệ. –

+0

^^ đó là với tiếng kêu táo. –

+0

Mặc dù Joel không chỉ định máy, [ông ấy cho thời gian] (http://boost-spirit.com/home/2013/02/23/spirit-x3-on-github/) để biên soạn ví dụ calc4 như ~ 5s. Ví dụ của bạn có vẻ phức tạp hơn, vì vậy thời gian bạn thấy có vẻ không hợp lý.Xem xét những gì bạn đang nhận được cho số lượng mã bạn đã viết (và số lượng công việc trình biên dịch phải làm) ... –

Trả lời

2

Trong khi điều này thực sự có thể là hồi quy trong Spi RIT X3 như @sehe cho thấy có một cách giải quyết với phiên bản hiện tại:

Thay đổi tất cả các quy tắc tham gia vào đệ quy biểu hiện như thế này:

const x3::rule<class block_term, ast::block_ast> block_term = "block_term"; 
auto const block_term_def = x3::rule<class block_term, ast::block_ast> {"block_term"} 
         = "(" >> expr >> ")"; 

BOOST_SPIRIT_DEFINE(block_term) 

Điều này làm giảm đáng kể thời gian biên dịch và phân tích cú pháp đang làm việc tốt. Hiệu suất của trình phân tích cú pháp dường như giống nhau (các kiểm thử rất không khoa học!).

+1

Điều đó có tác dụng vì nó phá vỡ sự khởi tạo đệ quy. Tuy nhiên, nó chưa được chứng minh là đáng tin cậy đối với tôi. Dường như các trình biên dịch đã trở nên quá thông minh về nội tuyến tích cực. Điều này thực sự là ý chính của một cách giải quyết (điều đáng tin cậy hơn là 'any_parser', nhưng điều đó mất tính tổng quát và kém thuận tiện hơn nhiều). Vẫn còn một vấn đề QOI tôi nghĩ. – sehe

4

này trông giống như một hồi quy nghiêm trọng với tôi.

Phải mất rất lâu trên máy tính của tôi:

  • gcc 5: chậm sử dụng bộ nhớ ngày càng nhiều lên đến 3GiB sau 4min30s, tiếp theo là giai đoạn lắp ráp của ~ 20s:

    g++-5 -std=c++14 -Wall -pedantic -Wextra -fsanitize=undefined,address -Wno-unused -g -O3 -isystem /home/sehe/custom/nonius/include -isystem /home/sehe/custom/boost_1_60_0 -pthread -march=native test.cpp -c -o test.o 
    test.cpp:119:62: warning: extra ‘;’ [-Wpedantic] 
        BOOST_SPIRIT_DEFINE(expr, add_expr, mult_expr, block_expr); 
                      ^
    g++-5 -std=c++14 -Wall -pedantic -Wextra -fsanitize=undefined,address -Wno-unused -g -O3 -isystem /home/sehe/custom/nonius/include -isystem /home/sehe/custom/boost_1_60_0 -pthread -march=native test.o -o test -L /home/sehe/custom/boost_1_60_0/stage/lib/ -Wl,-rpath,/home/sehe/custom/boost_1_60_0/stage/lib -lboost_system -lboost_regex -lboost_thread -lboost_iostreams -lboost_serialization -lboost_filesystem -lboost_chrono -lrt -lboost_unit_test_framework -lpugixml -lssl -lcrypto -lxml2 
    
    real 4m50.427s 
    user 4m48.248s 
    sys 0m1.856s 
    
  • clang 3.6: không thành công với chiều sâu mẫu đã vượt quá

    /home/sehe/custom/boost_1_60_0/boost/spirit/home/x3/support/context.hpp|30 col 25| fatal error: recursive template instantiation exceeded maximum depth of 256 
    

Điều này sau đó đưa ra một gợi ý trực tiếp về nguyên nhân gây ra nó.

linh cảm đầu tiên của tôi là x3::variant có thể dẫn đến các trình biên dịch để tích cực hơn những nội tuyến, nhưng replacing with boost::variant không giúp được gì nhiều:

g++-5 -std=c++14 -Wall -pedantic -Wextra -fsanitize=undefined,address -Wno-unused -g -O3 -isystem /home/sehe/custom/nonius/include -isystem /home/sehe/custom/boost_1_60_0 -pthread -march=native test.cpp -c -o test.o 
test.cpp:135:62: warning: extra ‘;’ [-Wpedantic] 
    BOOST_SPIRIT_DEFINE(expr, add_expr, mult_expr, block_expr); 
                  ^
g++-5 -std=c++14 -Wall -pedantic -Wextra -fsanitize=undefined,address -Wno-unused -g -O3 -isystem /home/sehe/custom/nonius/include -isystem /home/sehe/custom/boost_1_60_0 -pthread -march=native test.o -o test -L /home/sehe/custom/boost_1_60_0/stage/lib/ -Wl,-rpath,/home/sehe/custom/boost_1_60_0/stage/lib -lboost_system -lboost_regex -lboost_thread -lboost_iostreams -lboost_serialization -lboost_filesystem -lboost_chrono -lrt -lboost_unit_test_framework -lpugixml -lssl -lcrypto -lxml2 

real 3m55.728s 

Khi không có sự khác biệt trong resuts:

Ok: foo() result: foo() 
Fail: -foo() 
Ok: f1_2() result: f1_2() 
Ok: foo_bar() result: foo_bar() 
Ok: foo(bar (42, baz())) result: foo(bar(42,baz())) 
Ok: foo(5) result: foo(5) 
Ok: foo(-5) result: foo(-5) 
Ok: foo(1.1, foo(4.21e-2, 4., 6)) result: foo(1.100000,foo(0.042100,4.000000,6)) 
Ok: 1.1 result: 1.100000 
Ok: -1.1 result: -1.100000 
Ok: 1 * 1 result: (1 * 1) 
Ok: foo(1 * 1) * bar(42) result: (foo((1 * 1)) * bar(42)) 
Ok: foo(2 + 5.5, bar()*3.4-7) result: foo((2 + 5.500000),((bar() * 3.400000) - 7)) 
Ok: foo(2 + 5.5, bar(baz(-5/foo())) * 3.4 - 7) result: foo((2 + 5.500000),((bar(baz((-5/foo()))) * 3.400000) - 7)) 
Ok: 4 + 5 * 6 result: (4 + (5 * 6)) 
Ok: 1+2+3+4*5*6*-7+-8*+9-0 result: (1 + (2 + (3 + ((4 * (5 * (6 * -7))) + ((-8 * 9) - 0))))) 
Ok: (foo()) result: foo() 
Ok: foo() * ((1+2)+3*(2+3)) result: (foo() * ((1 + 2) + (3 * (2 + 3)))) 
Ok: (1+2)*3 result: ((1 + 2) * 3) 
Ok: 1+2*3 result: (1 + (2 * 3)) 
Fail: foo 

tôi 'd báo cáo điều này tại danh sách gửi thư linh thiêng: http://boost.2283326.n4.nabble.com/spirit-general-f2672582.html

Các vấn đề liên quan