Optimize RestrictionParser performance

This commit is contained in:
Siarhei Fedartsou 2022-08-28 09:10:33 +02:00
parent bfb74c2dad
commit c14461c273
2 changed files with 105 additions and 108 deletions

View File

@ -6,7 +6,9 @@
#include <boost/optional/optional.hpp> #include <boost/optional/optional.hpp>
#include <string> #include <string>
#include <set>
#include <vector> #include <vector>
#include <osmium/tags/filter.hpp>
namespace osmium namespace osmium
{ {
@ -43,7 +45,7 @@ class RestrictionParser
public: public:
RestrictionParser(bool use_turn_restrictions, RestrictionParser(bool use_turn_restrictions,
bool parse_conditionals, bool parse_conditionals,
std::vector<std::string> &restrictions); const std::vector<std::string> &restrictions);
std::vector<InputTurnRestriction> TryParse(const osmium::Relation &relation) const; std::vector<InputTurnRestriction> TryParse(const osmium::Relation &relation) const;
private: private:
@ -51,7 +53,8 @@ class RestrictionParser
bool use_turn_restrictions; bool use_turn_restrictions;
bool parse_conditionals; bool parse_conditionals;
std::vector<std::string> restrictions; std::set<std::string> restrictions;
osmium::tags::KeyFilter filter;
}; };
} // namespace extractor } // namespace extractor
} // namespace osrm } // namespace osrm

View File

@ -9,7 +9,6 @@
#include <boost/ref.hpp> #include <boost/ref.hpp>
#include <osmium/osm.hpp> #include <osmium/osm.hpp>
#include <osmium/tags/regex_filter.hpp>
#include <algorithm> #include <algorithm>
@ -20,9 +19,9 @@ namespace extractor
RestrictionParser::RestrictionParser(bool use_turn_restrictions_, RestrictionParser::RestrictionParser(bool use_turn_restrictions_,
bool parse_conditionals_, bool parse_conditionals_,
std::vector<std::string> &restrictions_) const std::vector<std::string> &restrictions_)
: use_turn_restrictions(use_turn_restrictions_), parse_conditionals(parse_conditionals_), : use_turn_restrictions(use_turn_restrictions_), parse_conditionals(parse_conditionals_),
restrictions(restrictions_) restrictions(restrictions_.begin(), restrictions_.end()), filter(false)
{ {
if (use_turn_restrictions) if (use_turn_restrictions)
{ {
@ -40,11 +39,28 @@ RestrictionParser::RestrictionParser(bool use_turn_restrictions_,
util::Log() << "Found no turn restriction tags"; util::Log() << "Found no turn restriction tags";
} }
} }
filter.add(true, "restriction");
if (parse_conditionals)
{
filter.add(true, "restriction:conditional");
for (const auto &namespaced : restrictions_)
{
filter.add(true, "restriction:" + namespaced + ":conditional");
}
}
// Not only use restriction= but also e.g. restriction:motorcar=
// Include restriction:{mode}:conditional if flagged
for (const auto &namespaced : restrictions_)
{
filter.add(true, "restriction:" + namespaced);
}
} }
/** /**
* Tries to parse a relation as a turn restriction. This can fail for a number of * Tries to parse a relation as a turn restriction. This can fail for a number of
* reasons. The return type is a boost::optional<T>. * reasons. The return type is a std::vector<T>.
* *
* Some restrictions can also be ignored: See the ```get_restrictions``` function * Some restrictions can also be ignored: See the ```get_restrictions``` function
* in the corresponding profile. We use it for both namespacing restrictions, as in * in the corresponding profile. We use it for both namespacing restrictions, as in
@ -59,31 +75,13 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const
return {}; return {};
} }
osmium::tags::KeyFilter filter(false);
filter.add(true, "restriction");
if (parse_conditionals)
{
filter.add(true, "restriction:conditional");
for (const auto &namespaced : restrictions)
{
filter.add(true, "restriction:" + namespaced + ":conditional");
}
}
// Not only use restriction= but also e.g. restriction:motorcar=
// Include restriction:{mode}:conditional if flagged
for (const auto &namespaced : restrictions)
{
filter.add(true, "restriction:" + namespaced);
}
const osmium::TagList &tag_list = relation.tags(); const osmium::TagList &tag_list = relation.tags();
osmium::tags::KeyFilter::iterator fi_begin(filter, tag_list.begin(), tag_list.end()); osmium::tags::KeyFilter::iterator fi_begin(filter, tag_list.begin(), tag_list.end());
osmium::tags::KeyFilter::iterator fi_end(filter, tag_list.end(), tag_list.end()); osmium::tags::KeyFilter::iterator fi_end(filter, tag_list.end(), tag_list.end());
// if it's not a restriction, continue; // if it's not a restriction, continue;
if (std::distance(fi_begin, fi_end) == 0) if (fi_begin == fi_end)
{ {
return {}; return {};
} }
@ -99,18 +97,19 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const
bool is_multi_from = false; bool is_multi_from = false;
bool is_multi_to = false; bool is_multi_to = false;
std::vector<util::OpeningHours> condition;
for (; fi_begin != fi_end; ++fi_begin) for (; fi_begin != fi_end; ++fi_begin)
{ {
const std::string key(fi_begin->key());
const std::string value(fi_begin->value()); const std::string value(fi_begin->value());
// documented OSM restriction tags start either with only_* or no_*; // documented OSM restriction tags start either with only_* or no_*;
// check and return on these values, and ignore no_*_on_red or unrecognized values // check and return on these values, and ignore no_*_on_red or unrecognized values
if (value.find("only_") == 0) if (boost::algorithm::starts_with(value, "only_"))
{ {
is_only_restriction = true; is_only_restriction = true;
} }
else if (value.find("no_") == 0 && !boost::algorithm::ends_with(value, "_on_red")) else if (boost::algorithm::starts_with(value, "no_") && !boost::algorithm::ends_with(value, "_on_red"))
{ {
is_only_restriction = false; is_only_restriction = false;
if (boost::algorithm::starts_with(value, "no_exit")) if (boost::algorithm::starts_with(value, "no_exit"))
@ -126,76 +125,8 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const
{ {
return {}; return {};
} }
}
constexpr auto INVALID_OSM_ID = std::numeric_limits<std::uint64_t>::max();
std::vector<OSMWayID> from_ways;
auto via_node = INVALID_OSM_ID;
std::vector<OSMWayID> via_ways;
std::vector<OSMWayID> to_ways;
bool is_node_restriction = true;
for (const auto &member : relation.members())
{
const char *role = member.role();
if (strcmp("from", role) != 0 && strcmp("to", role) != 0 && strcmp("via", role) != 0)
{
continue;
}
switch (member.type())
{
case osmium::item_type::node:
{
// Make sure nodes appear only in the role if a via node
if (0 == strcmp("from", role) || 0 == strcmp("to", role))
{
continue;
}
BOOST_ASSERT(0 == strcmp("via", role));
via_node = static_cast<std::uint64_t>(member.ref());
is_node_restriction = true;
// set via node id
break;
}
case osmium::item_type::way:
BOOST_ASSERT(0 == strcmp("from", role) || 0 == strcmp("to", role) ||
0 == strcmp("via", role));
if (0 == strcmp("from", role))
{
from_ways.push_back({static_cast<std::uint64_t>(member.ref())});
}
else if (0 == strcmp("to", role))
{
to_ways.push_back({static_cast<std::uint64_t>(member.ref())});
}
else if (0 == strcmp("via", role))
{
via_ways.push_back({static_cast<std::uint64_t>(member.ref())});
is_node_restriction = false;
}
break;
case osmium::item_type::relation:
// not yet supported, but who knows what the future holds...
break;
default:
// shouldn't ever happen
break;
}
}
std::vector<util::OpeningHours> condition;
// parse conditional tags
if (parse_conditionals)
{
osmium::tags::KeyFilter::iterator fi_begin(filter, tag_list.begin(), tag_list.end());
osmium::tags::KeyFilter::iterator fi_end(filter, tag_list.end(), tag_list.end());
for (; fi_begin != fi_end; ++fi_begin)
{
const std::string key(fi_begin->key());
const std::string value(fi_begin->value());
if (parse_conditionals) {
// Parse condition and add independent value/condition pairs // Parse condition and add independent value/condition pairs
const auto &parsed = osrm::util::ParseConditionalRestrictions(value); const auto &parsed = osrm::util::ParseConditionalRestrictions(value);
@ -214,6 +145,66 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const
} }
} }
constexpr auto INVALID_OSM_ID = std::numeric_limits<std::uint64_t>::max();
std::vector<OSMWayID> from_ways;
auto via_node = INVALID_OSM_ID;
std::vector<OSMWayID> via_ways;
std::vector<OSMWayID> to_ways;
bool is_node_restriction = true;
for (const auto &member : relation.members())
{
const char *role = member.role();
const bool is_from_role = strcmp("from", role) == 0;
const bool is_to_role = strcmp("to", role) == 0;
const bool is_via_role = strcmp("via", role) == 0;
if (!is_from_role && !is_to_role && !is_via_role)
{
continue;
}
switch (member.type())
{
case osmium::item_type::node:
{
// Make sure nodes appear only in the role if a via node
if (is_from_role || is_to_role)
{
continue;
}
BOOST_ASSERT(is_via_role);
via_node = static_cast<std::uint64_t>(member.ref());
is_node_restriction = true;
// set via node id
break;
}
case osmium::item_type::way:
BOOST_ASSERT(is_from_role || is_to_role || is_via_role);
if (is_from_role)
{
from_ways.push_back({static_cast<std::uint64_t>(member.ref())});
}
else if (is_to_role)
{
to_ways.push_back({static_cast<std::uint64_t>(member.ref())});
}
else if (is_via_role)
{
via_ways.push_back({static_cast<std::uint64_t>(member.ref())});
is_node_restriction = false;
}
break;
case osmium::item_type::relation:
// not yet supported, but who knows what the future holds...
break;
default:
// shouldn't ever happen
break;
}
}
std::vector<InputTurnRestriction> restriction_containers; std::vector<InputTurnRestriction> restriction_containers;
if (!from_ways.empty() && (via_node != INVALID_OSM_ID || !via_ways.empty()) && !to_ways.empty()) if (!from_ways.empty() && (via_node != INVALID_OSM_ID || !via_ways.empty()) && !to_ways.empty())
{ {
@ -270,17 +261,20 @@ bool RestrictionParser::ShouldIgnoreRestriction(const std::string &except_tag_st
return false; return false;
} }
// Be warned, this is quadratic work here, but we assume that // split `except_tag_string` by semicolon and check if any of items is in `restrictions`
// only a few exceptions are actually defined. std::string current_string;
const std::regex delimiter_re("[;][ ]*"); for (size_t index = 0; index < except_tag_string.size(); index++) {
std::sregex_token_iterator except_tags_begin( const auto ch = except_tag_string[index];
except_tag_string.begin(), except_tag_string.end(), delimiter_re, -1); if (ch != ';') {
std::sregex_token_iterator except_tags_end; current_string += ch;
} else {
return std::any_of(except_tags_begin, except_tags_end, [&](const std::string &current_string) { if (restrictions.find(current_string) != restrictions.end()) {
return std::end(restrictions) != return true;
std::find(std::begin(restrictions), std::end(restrictions), current_string); }
}); current_string.clear();
}
}
return restrictions.find(current_string) != restrictions.end();
} }
} // namespace extractor } // namespace extractor
} // namespace osrm } // namespace osrm