2

最近、cpp-netlib の学習を開始し、netlibs サンプル プログラムの 1 つをテストしています。

#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

これは、私が投稿した古い質問へのリンクです。HTTP クライアント本体を使用するときに、このプログラムが cpp-Netlib を Boost Mac OSX seg fault でコンパイルするために必要なすべてを見つける方法を詳しく説明しています。

コンパイルに約 15 秒かかることがわかりました。このプロセスを高速化する方法があるかどうか疑問に思っていましたか? このコードをコンパイルするのは本当に遅いのでしょうか、それともリンカがこれらすべてのライブラリを取得するのにかなりの時間がかかりますか? もしそうなら、これを高速化できますか?

4

1 に答える 1

3

cpp-netlib が URL 解析に Spirit Qi グラマートを使用していることを覚えているようです。

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

この場合、スローダウンはkey_value_sequenceパーサーのようですaccessors.hpp

これらは非常にテンプレートが多く、コンパイルにかなりの時間がかかりますが、使用するコンパイラにわずかに依存します (私の経験では、MSVC が最悪です)。

これらのヘッダーが含まれないようにすることができます。少なくとも、cppそれを必要とする翻訳単位 ( s) にのみ含めてください。「一般的な」ヘッダーの依存関係に巻き込まれないようにしてください。これは、コンパイラが各反復でそのようなものを再コンパイルする必要があることを意味します (プリコンパイル済みヘッダーを使用しても、コストはかなりのものになる可能性があります)。


コンパイラのバージョンによっては、次のことが役立ちます。

  • デバッグ情報を無効にする (-g0)
  • サイズの最適化 (-Os)
  • BOOST_SPIRIT_USE_PHOENIX_V3 を定義します (~1.58 以降のデフォルト)
  • より小さな数値のようなものを定義FUSION_MAX_VECTOR_SIZEします (デフォルト: 10)

実際、c++14 対応の clang を使用している場合は、Qi の代わりに Spirit X3 を使用するパッチをテストすることに興味があります。

少なくともこの 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 秒に短縮されます

BIG FAT WARNING X3 コードはテストされていません (何がそれを使用しているのかさえわかりません。私の知る限り、「やみくもに」X3 に変換しただけです)。

于 2015-12-05T09:58:00.033 に答える