Skip to content

Commit 9a638f3

Browse files
Optimize RestrictionParser performance (#6344)
1 parent 2cf9571 commit 9a638f3

File tree

3 files changed

+83
-77
lines changed

3 files changed

+83
-77
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
- NodeJS:
1010
- FIXED: Support `skip_waypoints` in Node bindings [#6060](https://github.com/Project-OSRM/osrm-backend/pull/6060)
1111
- Misc:
12+
- CHANGED: Optimize RestrictionParser performance. [#6344](https://github.com/Project-OSRM/osrm-backend/pull/6344)
1213
- ADDED: Support floats for speed value in traffic updates CSV. [#6327](https://github.com/Project-OSRM/osrm-backend/pull/6327)
1314
- CHANGED: Use Lua 5.4 in Docker image. [#6346](https://github.com/Project-OSRM/osrm-backend/pull/6346)
1415
- CHANGED: Remove redundant nullptr check. [#6326](https://github.com/Project-OSRM/osrm-backend/pull/6326)

include/extractor/restriction_parser.hpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
#include <boost/optional/optional.hpp>
77

8+
#include <osmium/tags/filter.hpp>
9+
#include <set>
810
#include <string>
911
#include <vector>
1012

@@ -43,15 +45,16 @@ class RestrictionParser
4345
public:
4446
RestrictionParser(bool use_turn_restrictions,
4547
bool parse_conditionals,
46-
std::vector<std::string> &restrictions);
48+
const std::vector<std::string> &restrictions);
4749
std::vector<InputTurnRestriction> TryParse(const osmium::Relation &relation) const;
4850

4951
private:
5052
bool ShouldIgnoreRestriction(const std::string &except_tag_string) const;
5153

5254
bool use_turn_restrictions;
5355
bool parse_conditionals;
54-
std::vector<std::string> restrictions;
56+
std::set<std::string> restrictions;
57+
osmium::tags::KeyFilter filter;
5558
};
5659
} // namespace extractor
5760
} // namespace osrm

src/extractor/restriction_parser.cpp

Lines changed: 77 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
#include "extractor/profile_properties.hpp"
33

44
#include "util/conditional_restrictions.hpp"
5+
#include "util/integer_range.hpp"
56
#include "util/log.hpp"
67

78
#include <boost/algorithm/string/predicate.hpp>
89
#include <boost/optional/optional.hpp>
910
#include <boost/ref.hpp>
1011

1112
#include <osmium/osm.hpp>
12-
#include <osmium/tags/regex_filter.hpp>
1313

1414
#include <algorithm>
1515

@@ -20,9 +20,9 @@ namespace extractor
2020

2121
RestrictionParser::RestrictionParser(bool use_turn_restrictions_,
2222
bool parse_conditionals_,
23-
std::vector<std::string> &restrictions_)
23+
const std::vector<std::string> &restrictions_)
2424
: use_turn_restrictions(use_turn_restrictions_), parse_conditionals(parse_conditionals_),
25-
restrictions(restrictions_)
25+
restrictions(restrictions_.begin(), restrictions_.end()), filter(false)
2626
{
2727
if (use_turn_restrictions)
2828
{
@@ -40,11 +40,28 @@ RestrictionParser::RestrictionParser(bool use_turn_restrictions_,
4040
util::Log() << "Found no turn restriction tags";
4141
}
4242
}
43+
44+
filter.add(true, "restriction");
45+
if (parse_conditionals)
46+
{
47+
filter.add(true, "restriction:conditional");
48+
for (const auto &namespaced : restrictions_)
49+
{
50+
filter.add(true, "restriction:" + namespaced + ":conditional");
51+
}
52+
}
53+
54+
// Not only use restriction= but also e.g. restriction:motorcar=
55+
// Include restriction:{mode}:conditional if flagged
56+
for (const auto &namespaced : restrictions_)
57+
{
58+
filter.add(true, "restriction:" + namespaced);
59+
}
4360
}
4461

4562
/**
4663
* Tries to parse a relation as a turn restriction. This can fail for a number of
47-
* reasons. The return type is a boost::optional<T>.
64+
* reasons. The return type is a std::vector<T>.
4865
*
4966
* Some restrictions can also be ignored: See the ```get_restrictions``` function
5067
* in the corresponding profile. We use it for both namespacing restrictions, as in
@@ -59,31 +76,13 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const
5976
return {};
6077
}
6178

62-
osmium::tags::KeyFilter filter(false);
63-
filter.add(true, "restriction");
64-
if (parse_conditionals)
65-
{
66-
filter.add(true, "restriction:conditional");
67-
for (const auto &namespaced : restrictions)
68-
{
69-
filter.add(true, "restriction:" + namespaced + ":conditional");
70-
}
71-
}
72-
73-
// Not only use restriction= but also e.g. restriction:motorcar=
74-
// Include restriction:{mode}:conditional if flagged
75-
for (const auto &namespaced : restrictions)
76-
{
77-
filter.add(true, "restriction:" + namespaced);
78-
}
79-
8079
const osmium::TagList &tag_list = relation.tags();
8180

8281
osmium::tags::KeyFilter::iterator fi_begin(filter, tag_list.begin(), tag_list.end());
8382
osmium::tags::KeyFilter::iterator fi_end(filter, tag_list.end(), tag_list.end());
8483

8584
// if it's not a restriction, continue;
86-
if (std::distance(fi_begin, fi_end) == 0)
85+
if (fi_begin == fi_end)
8786
{
8887
return {};
8988
}
@@ -99,18 +98,20 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const
9998
bool is_multi_from = false;
10099
bool is_multi_to = false;
101100

101+
std::vector<util::OpeningHours> condition;
102+
102103
for (; fi_begin != fi_end; ++fi_begin)
103104
{
104-
const std::string key(fi_begin->key());
105-
const std::string value(fi_begin->value());
105+
auto value = fi_begin->value();
106106

107107
// documented OSM restriction tags start either with only_* or no_*;
108108
// check and return on these values, and ignore no_*_on_red or unrecognized values
109-
if (value.find("only_") == 0)
109+
if (boost::algorithm::starts_with(value, "only_"))
110110
{
111111
is_only_restriction = true;
112112
}
113-
else if (value.find("no_") == 0 && !boost::algorithm::ends_with(value, "_on_red"))
113+
else if (boost::algorithm::starts_with(value, "no_") &&
114+
!boost::algorithm::ends_with(value, "_on_red"))
114115
{
115116
is_only_restriction = false;
116117
if (boost::algorithm::starts_with(value, "no_exit"))
@@ -126,6 +127,25 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const
126127
{
127128
return {};
128129
}
130+
131+
if (parse_conditionals)
132+
{
133+
// Parse condition and add independent value/condition pairs
134+
const auto &parsed = osrm::util::ParseConditionalRestrictions(value);
135+
136+
if (parsed.empty())
137+
continue;
138+
139+
for (const auto &p : parsed)
140+
{
141+
std::vector<util::OpeningHours> hours = util::ParseOpeningHours(p.condition);
142+
// found unrecognized condition, continue
143+
if (hours.empty())
144+
return {};
145+
146+
condition = std::move(hours);
147+
}
148+
}
129149
}
130150

131151
constexpr auto INVALID_OSM_ID = std::numeric_limits<std::uint64_t>::max();
@@ -138,7 +158,11 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const
138158
for (const auto &member : relation.members())
139159
{
140160
const char *role = member.role();
141-
if (strcmp("from", role) != 0 && strcmp("to", role) != 0 && strcmp("via", role) != 0)
161+
const bool is_from_role = strcmp("from", role) == 0;
162+
const bool is_to_role = strcmp("to", role) == 0;
163+
const bool is_via_role = strcmp("via", role) == 0;
164+
165+
if (!is_from_role && !is_to_role && !is_via_role)
142166
{
143167
continue;
144168
}
@@ -149,28 +173,27 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const
149173
{
150174

151175
// Make sure nodes appear only in the role if a via node
152-
if (0 == strcmp("from", role) || 0 == strcmp("to", role))
176+
if (is_from_role || is_to_role)
153177
{
154178
continue;
155179
}
156-
BOOST_ASSERT(0 == strcmp("via", role));
180+
BOOST_ASSERT(is_via_role);
157181
via_node = static_cast<std::uint64_t>(member.ref());
158182
is_node_restriction = true;
159183
// set via node id
160184
break;
161185
}
162186
case osmium::item_type::way:
163-
BOOST_ASSERT(0 == strcmp("from", role) || 0 == strcmp("to", role) ||
164-
0 == strcmp("via", role));
165-
if (0 == strcmp("from", role))
187+
BOOST_ASSERT(is_from_role || is_to_role || is_via_role);
188+
if (is_from_role)
166189
{
167190
from_ways.push_back({static_cast<std::uint64_t>(member.ref())});
168191
}
169-
else if (0 == strcmp("to", role))
192+
else if (is_to_role)
170193
{
171194
to_ways.push_back({static_cast<std::uint64_t>(member.ref())});
172195
}
173-
else if (0 == strcmp("via", role))
196+
else if (is_via_role)
174197
{
175198
via_ways.push_back({static_cast<std::uint64_t>(member.ref())});
176199
is_node_restriction = false;
@@ -185,35 +208,6 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const
185208
}
186209
}
187210

188-
std::vector<util::OpeningHours> condition;
189-
// parse conditional tags
190-
if (parse_conditionals)
191-
{
192-
osmium::tags::KeyFilter::iterator fi_begin(filter, tag_list.begin(), tag_list.end());
193-
osmium::tags::KeyFilter::iterator fi_end(filter, tag_list.end(), tag_list.end());
194-
for (; fi_begin != fi_end; ++fi_begin)
195-
{
196-
const std::string key(fi_begin->key());
197-
const std::string value(fi_begin->value());
198-
199-
// Parse condition and add independent value/condition pairs
200-
const auto &parsed = osrm::util::ParseConditionalRestrictions(value);
201-
202-
if (parsed.empty())
203-
continue;
204-
205-
for (const auto &p : parsed)
206-
{
207-
std::vector<util::OpeningHours> hours = util::ParseOpeningHours(p.condition);
208-
// found unrecognized condition, continue
209-
if (hours.empty())
210-
return {};
211-
212-
condition = std::move(hours);
213-
}
214-
}
215-
}
216-
217211
std::vector<InputTurnRestriction> restriction_containers;
218212
if (!from_ways.empty() && (via_node != INVALID_OSM_ID || !via_ways.empty()) && !to_ways.empty())
219213
{
@@ -270,17 +264,25 @@ bool RestrictionParser::ShouldIgnoreRestriction(const std::string &except_tag_st
270264
return false;
271265
}
272266

273-
// Be warned, this is quadratic work here, but we assume that
274-
// only a few exceptions are actually defined.
275-
const std::regex delimiter_re("[;][ ]*");
276-
std::sregex_token_iterator except_tags_begin(
277-
except_tag_string.begin(), except_tag_string.end(), delimiter_re, -1);
278-
std::sregex_token_iterator except_tags_end;
279-
280-
return std::any_of(except_tags_begin, except_tags_end, [&](const std::string &current_string) {
281-
return std::end(restrictions) !=
282-
std::find(std::begin(restrictions), std::end(restrictions), current_string);
283-
});
267+
// split `except_tag_string` by semicolon and check if any of items is in `restrictions`
268+
std::string current_string;
269+
for (auto index : util::irange<size_t>(0, except_tag_string.size()))
270+
{
271+
const auto ch = except_tag_string[index];
272+
if (ch != ';')
273+
{
274+
current_string += ch;
275+
}
276+
else
277+
{
278+
if (restrictions.find(current_string) != restrictions.end())
279+
{
280+
return true;
281+
}
282+
current_string.clear();
283+
}
284+
}
285+
return restrictions.find(current_string) != restrictions.end();
284286
}
285287
} // namespace extractor
286288
} // namespace osrm

0 commit comments

Comments
 (0)