I seem to remember cpp-netlib uses Spirit Qi grammarts for URL parsing e.g.
network/uri/accessors.hpp
network/uri/uri.ipp
In this case the slowdown seems to be the key_value_sequence
parser in accessors.hpp
These are very template heavy and take considerable time to compile, depending only slightly on the compiler used (MSVC being worst, in my experience).
You could prevent inclusion of these headers. At the very least only include them in the translation units (cpp
s) that require it; don't ever make it get caught up in your "common" header dependencies. That would mean the compiler has to recompile that stuff on each iteration (even using precompiled headers the cost would likely be significant).
Depending on your compiler version, these could help:
- disable debug information (-g0)
- optimize for size (-Os)
- define BOOST_SPIRIT_USE_PHOENIX_V3 (default since ~1.58)
- define things like
FUSION_MAX_VECTOR_SIZE
to smaller numbers (default: 10)
Really, if you use c++14 capable clang, I'd be interested in testing a patch to use Spirit X3 instead of Qi.
At least replacing this Qi parser:
#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;
}
With this X3 variant:
#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;
}
Reduces compilation time from ~8s down to ~5s on my system
BIG FAT WARNING The X3 code is untested (I don't even know what uses it, I just "blindly" translated to X3 to the best of my knowledge)