Почему моя простая программа cpp-netlib так долго компилируется?

Я недавно начал изучать cpp-netlib и тестирую одну из примеров программ netlibs.

#include <boost/network/protocol/http/client.hpp>
#include <iostream>
int main()
{
    using namespace boost::network;
    http::client client;
    http::client::request request("http://www.boost.org");
    request << header("Connection", "close");
    http::client::response response = client.get(request);
    std::cout << body(response) << std::endl;
    return 0;
}

После многих часов исследований я обнаружил, что правильная команда, которую мне нужно использовать для компиляции моей программы,

clang++ -std=c++11 -I /usr/local/Cellar/openssl/1.0.2e/include test.cpp -L /usr/local/Cellar/openssl/1.0.2e/lib -lboost_system-mt -lboost_thread-mt -lcppnetlib-client-connections -lcppnetlib-uri -lcppnetlib-server-parsers -lssl -lcrypto

Вот ссылка на старый вопрос, который я опубликовал, подробно рассказывающий, как я нашел все, что нужно этой программе для компиляции ошибки cpp-Netlib с Boost Mac OSX при использовании тела клиента HTTP

Я считаю, что для компиляции требуется около 15 секунд, и мне было интересно, есть ли способ ускорить этот процесс? Это действительно так медленно, чтобы скомпилировать этот код, или компоновщик занимает достаточно много времени, чтобы пойти и получить все эти библиотеки, и если да, могу ли я ускорить это?

1 ответ

Решение

Кажется, я помню, что cpp-netlib использует грамматику Spirit Qi для разбора URL, например

network/uri/accessors.hpp
network/uri/uri.ipp

В этом случае замедление кажется key_value_sequence парсер в accessors.hpp

Они очень трудоемки и требуют значительного времени для компиляции, в зависимости лишь от используемого компилятора (на мой взгляд, MSVC был худшим).

Вы можете предотвратить включение этих заголовков. По крайней мере, включить их только в единицы перевода (cppу) которые этого требуют; никогда не заставляйте его попадать в ваши "общие" зависимости заголовка. Это означало бы, что компилятор должен перекомпилировать эти вещи на каждой итерации (даже с использованием предварительно скомпилированных заголовков стоимость, вероятно, будет значительной).


В зависимости от версии вашего компилятора они могут помочь:

  • отключить отладочную информацию (-g0)
  • оптимизировать по размеру (-Os)
  • определить BOOST_SPIRIT_USE_PHOENIX_V3 (по умолчанию с ~1.58)
  • определить такие вещи, как FUSION_MAX_VECTOR_SIZE на меньшие номера (по умолчанию: 10)

На самом деле, если вы используете C++14-совместимый кланг, мне было бы интересно протестировать патч для использования Spirit X3 вместо Qi.

По крайней мере, заменив этот парсер Qi:

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

// ...
namespace details {
template <typename Map>
struct key_value_sequence : spirit::qi::grammar<uri::const_iterator, Map()> {
  typedef typename Map::key_type key_type;
  typedef typename Map::mapped_type mapped_type;
  typedef std::pair<key_type, mapped_type> pair_type;

  key_value_sequence() : key_value_sequence::base_type(query) {
    query = pair >> *((spirit::qi::lit(';') | '&') >> pair);
    pair = key >> -('=' >> value);
    key =
        spirit::qi::char_("a-zA-Z_") >> *spirit::qi::char_("-+.~a-zA-Z_0-9/%");
    value = +spirit::qi::char_("-+.~a-zA-Z_0-9/%");
  }

  spirit::qi::rule<uri::const_iterator, Map()> query;
  spirit::qi::rule<uri::const_iterator, pair_type()> pair;
  spirit::qi::rule<uri::const_iterator, key_type()> key;
  spirit::qi::rule<uri::const_iterator, mapped_type()> value;
};
}  // namespace details

template <class Map>
inline Map &query_map(const uri &uri_, Map &map) {
  const uri::string_type range = uri_.query();
  details::key_value_sequence<Map> parser;
  spirit::qi::parse(boost::begin(range), boost::end(range), parser, map);
  return map;
}

С этим вариантом X3:

#include <boost/spirit/home/x3.hpp>

// ...
namespace details {
    namespace kvs_parser {
        namespace x3 = boost::spirit::x3;

        static auto const key    = x3::char_("a-zA-Z_") >> *x3::char_("-+.~a-zA-Z_0-9/%");
        static auto const value  = +x3::char_("-+.~a-zA-Z_0-9/%");

        template <typename Map, typename K = typename Map::key_type, typename V = typename Map::mapped_type, typename Pair = std::pair<K, V> >
        static auto const pair   = x3::rule<struct kvs_pair, Pair> {} 
                                = key >> -('=' >> value);

        template <typename Map>
        static auto const query  = pair<Map> >> *((x3::lit(';') | '&') >> pair<Map>);
    }
}  // namespace details

template <class Map>
inline Map &query_map(const uri &uri_, Map &map) {
    const uri::string_type range = uri_.query();
    spirit::x3::parse(boost::begin(range), boost::end(range), details::kvs_parser::query<Map>, map);
    return map;
}

Сокращает время компиляции с ~8 с до ~5 с в моей системе

БОЛЬШОЕ ЖИРНОЕ ПРЕДУПРЕЖДЕНИЕ Код X3 не проверен (я даже не знаю, что его использует, я просто "вслепую" перевел на X3, насколько мне известно)

Другие вопросы по тегам