Skip to content

Commit 27ed69b

Browse files
TheMarexPatrick Niklaus
authored andcommitted
Parallize scripting on osmium::Buffer granularity
Fixes #3447 and reduces parsing time by about 15%.
1 parent cd8fb82 commit 27ed69b

File tree

4 files changed

+119
-108
lines changed

4 files changed

+119
-108
lines changed

include/extractor/scripting_environment.hpp

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,12 @@ class ScriptingEnvironment
5858
virtual void ProcessTurn(ExtractionTurn &turn) = 0;
5959
virtual void ProcessSegment(ExtractionSegment &segment) = 0;
6060

61-
virtual void
62-
ProcessElements(const std::vector<osmium::memory::Buffer::const_iterator> &osm_elements,
63-
const RestrictionParser &restriction_parser,
64-
tbb::concurrent_vector<std::pair<std::size_t, ExtractionNode>> &resulting_nodes,
65-
tbb::concurrent_vector<std::pair<std::size_t, ExtractionWay>> &resulting_ways,
66-
tbb::concurrent_vector<boost::optional<InputRestrictionContainer>>
67-
&resulting_restrictions) = 0;
61+
virtual void ProcessElements(
62+
const osmium::memory::Buffer &buffer,
63+
const RestrictionParser &restriction_parser,
64+
std::vector<std::pair<const osmium::Node &, ExtractionNode>> &resulting_nodes,
65+
std::vector<std::pair<const osmium::Way &, ExtractionWay>> &resulting_ways,
66+
std::vector<boost::optional<InputRestrictionContainer>> &resulting_restrictions) = 0;
6867
};
6968
}
7069
}

include/extractor/scripting_environment_lua.hpp

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,12 @@ class Sol2ScriptingEnvironment final : public ScriptingEnvironment
6565
void ProcessTurn(ExtractionTurn &turn) override;
6666
void ProcessSegment(ExtractionSegment &segment) override;
6767

68-
void
69-
ProcessElements(const std::vector<osmium::memory::Buffer::const_iterator> &osm_elements,
70-
const RestrictionParser &restriction_parser,
71-
tbb::concurrent_vector<std::pair<std::size_t, ExtractionNode>> &resulting_nodes,
72-
tbb::concurrent_vector<std::pair<std::size_t, ExtractionWay>> &resulting_ways,
73-
tbb::concurrent_vector<boost::optional<InputRestrictionContainer>>
74-
&resulting_restrictions) override;
68+
void ProcessElements(
69+
const osmium::memory::Buffer &buffer,
70+
const RestrictionParser &restriction_parser,
71+
std::vector<std::pair<const osmium::Node &, ExtractionNode>> &resulting_nodes,
72+
std::vector<std::pair<const osmium::Way &, ExtractionWay>> &resulting_ways,
73+
std::vector<boost::optional<InputRestrictionContainer>> &resulting_restrictions) override;
7574

7675
private:
7776
void InitContext(LuaScriptingContext &context);

src/extractor/extractor.cpp

Lines changed: 63 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,13 @@
3333

3434
#include <boost/filesystem.hpp>
3535
#include <boost/filesystem/fstream.hpp>
36+
#include <boost/iterator/function_input_iterator.hpp>
3637
#include <boost/optional/optional.hpp>
3738
#include <boost/scope_exit.hpp>
3839

3940
#include <osmium/io/any_input.hpp>
4041

41-
#include <tbb/concurrent_vector.h>
42+
#include <tbb/pipeline.h>
4243
#include <tbb/task_scheduler_init.h>
4344

4445
#include <cstdlib>
@@ -252,58 +253,77 @@ std::vector<TurnRestriction> Extractor::ParseOSMData(ScriptingEnvironment &scrip
252253

253254
timestamp_file.WriteFrom(timestamp.c_str(), timestamp.length());
254255

255-
// initialize vectors holding parsed objects
256-
tbb::concurrent_vector<std::pair<std::size_t, ExtractionNode>> resulting_nodes;
257-
tbb::concurrent_vector<std::pair<std::size_t, ExtractionWay>> resulting_ways;
258-
tbb::concurrent_vector<boost::optional<InputRestrictionContainer>> resulting_restrictions;
259-
260256
std::vector<std::string> restrictions = scripting_environment.GetRestrictions();
261257
// setup restriction parser
262258
const RestrictionParser restriction_parser(
263259
scripting_environment.GetProfileProperties().use_turn_restrictions,
264260
config.parse_conditionals,
265261
restrictions);
266262

267-
// create a vector of iterators into the buffer
268-
for (std::vector<osmium::memory::Buffer::const_iterator> osm_elements;
269-
const osmium::memory::Buffer buffer = reader.read();
270-
osm_elements.clear())
271-
{
272-
for (auto iter = std::begin(buffer), end = std::end(buffer); iter != end; ++iter)
273-
{
274-
osm_elements.push_back(iter);
275-
}
263+
std::mutex process_mutex;
276264

277-
// clear resulting vectors
278-
resulting_nodes.clear();
279-
resulting_ways.clear();
280-
resulting_restrictions.clear();
265+
using SharedBuffer = std::shared_ptr<const osmium::memory::Buffer>;
266+
struct ParsedBuffer
267+
{
268+
SharedBuffer buffer;
269+
std::vector<std::pair<const osmium::Node &, ExtractionNode>> resulting_nodes;
270+
std::vector<std::pair<const osmium::Way &, ExtractionWay>> resulting_ways;
271+
std::vector<boost::optional<InputRestrictionContainer>> resulting_restrictions;
272+
};
281273

282-
scripting_environment.ProcessElements(osm_elements,
283-
restriction_parser,
284-
resulting_nodes,
285-
resulting_ways,
286-
resulting_restrictions);
274+
tbb::filter_t<void, SharedBuffer> buffer_reader(
275+
tbb::filter::serial_in_order, [&](tbb::flow_control &fc) {
276+
if (auto buffer = reader.read())
277+
{
278+
return std::make_shared<const osmium::memory::Buffer>(std::move(buffer));
279+
}
280+
else
281+
{
282+
fc.stop();
283+
return SharedBuffer{};
284+
}
285+
});
286+
tbb::filter_t<SharedBuffer, std::shared_ptr<ParsedBuffer>> buffer_transform(
287+
tbb::filter::parallel, [&](const SharedBuffer buffer) {
288+
if (!buffer)
289+
return std::shared_ptr<ParsedBuffer>{};
290+
291+
auto parsed_buffer = std::make_shared<ParsedBuffer>();
292+
parsed_buffer->buffer = buffer;
293+
scripting_environment.ProcessElements(*buffer,
294+
restriction_parser,
295+
parsed_buffer->resulting_nodes,
296+
parsed_buffer->resulting_ways,
297+
parsed_buffer->resulting_restrictions);
298+
return parsed_buffer;
299+
});
300+
tbb::filter_t<std::shared_ptr<ParsedBuffer>, void> buffer_storage(
301+
tbb::filter::serial_in_order, [&](const std::shared_ptr<ParsedBuffer> parsed_buffer) {
302+
if (!parsed_buffer)
303+
return;
304+
305+
number_of_nodes += parsed_buffer->resulting_nodes.size();
306+
// put parsed objects thru extractor callbacks
307+
for (const auto &result : parsed_buffer->resulting_nodes)
308+
{
309+
extractor_callbacks->ProcessNode(result.first, result.second);
310+
}
311+
number_of_ways += parsed_buffer->resulting_ways.size();
312+
for (const auto &result : parsed_buffer->resulting_ways)
313+
{
314+
extractor_callbacks->ProcessWay(result.first, result.second);
315+
}
316+
number_of_relations += parsed_buffer->resulting_restrictions.size();
317+
for (const auto &result : parsed_buffer->resulting_restrictions)
318+
{
319+
extractor_callbacks->ProcessRestriction(result);
320+
}
321+
});
322+
323+
// Number of pipeline tokens that yielded the best speedup was about 1.5 * num_cores
324+
tbb::parallel_pipeline(tbb::task_scheduler_init::default_num_threads() * 1.5,
325+
buffer_reader & buffer_transform & buffer_storage);
287326

288-
number_of_nodes += resulting_nodes.size();
289-
// put parsed objects thru extractor callbacks
290-
for (const auto &result : resulting_nodes)
291-
{
292-
extractor_callbacks->ProcessNode(
293-
static_cast<const osmium::Node &>(*(osm_elements[result.first])), result.second);
294-
}
295-
number_of_ways += resulting_ways.size();
296-
for (const auto &result : resulting_ways)
297-
{
298-
extractor_callbacks->ProcessWay(
299-
static_cast<const osmium::Way &>(*(osm_elements[result.first])), result.second);
300-
}
301-
number_of_relations += resulting_restrictions.size();
302-
for (const auto &result : resulting_restrictions)
303-
{
304-
extractor_callbacks->ProcessRestriction(result);
305-
}
306-
}
307327
TIMER_STOP(parsing);
308328
util::Log() << "Parsing finished after " << TIMER_SEC(parsing) << " seconds";
309329

src/extractor/scripting_environment_lua.cpp

Lines changed: 44 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -489,61 +489,54 @@ LuaScriptingContext &Sol2ScriptingEnvironment::GetSol2Context()
489489
}
490490

491491
void Sol2ScriptingEnvironment::ProcessElements(
492-
const std::vector<osmium::memory::Buffer::const_iterator> &osm_elements,
492+
const osmium::memory::Buffer &buffer,
493493
const RestrictionParser &restriction_parser,
494-
tbb::concurrent_vector<std::pair<std::size_t, ExtractionNode>> &resulting_nodes,
495-
tbb::concurrent_vector<std::pair<std::size_t, ExtractionWay>> &resulting_ways,
496-
tbb::concurrent_vector<boost::optional<InputRestrictionContainer>> &resulting_restrictions)
494+
std::vector<std::pair<const osmium::Node &, ExtractionNode>> &resulting_nodes,
495+
std::vector<std::pair<const osmium::Way &, ExtractionWay>> &resulting_ways,
496+
std::vector<boost::optional<InputRestrictionContainer>> &resulting_restrictions)
497497
{
498-
// parse OSM entities in parallel, store in resulting vectors
499-
tbb::parallel_for(
500-
tbb::blocked_range<std::size_t>(0, osm_elements.size()),
501-
[&](const tbb::blocked_range<std::size_t> &range) {
502-
ExtractionNode result_node;
503-
ExtractionWay result_way;
504-
std::vector<InputRestrictionContainer> result_res;
505-
auto &local_context = this->GetSol2Context();
506-
507-
for (auto x = range.begin(), end = range.end(); x != end; ++x)
498+
ExtractionNode result_node;
499+
ExtractionWay result_way;
500+
std::vector<InputRestrictionContainer> result_res;
501+
auto &local_context = this->GetSol2Context();
502+
503+
for (auto entity = buffer.cbegin(), end = buffer.cend(); entity != end; ++entity)
504+
{
505+
switch (entity->type())
506+
{
507+
case osmium::item_type::node:
508+
result_node.clear();
509+
if (local_context.has_node_function &&
510+
(!static_cast<const osmium::Node &>(*entity).tags().empty() ||
511+
local_context.properties.call_tagless_node_function))
508512
{
509-
const auto entity = osm_elements[x];
510-
511-
switch (entity->type())
512-
{
513-
case osmium::item_type::node:
514-
result_node.clear();
515-
if (local_context.has_node_function &&
516-
(!static_cast<const osmium::Node &>(*entity).tags().empty() ||
517-
local_context.properties.call_tagless_node_function))
518-
{
519-
local_context.ProcessNode(static_cast<const osmium::Node &>(*entity),
520-
result_node);
521-
}
522-
resulting_nodes.push_back(std::make_pair(x, std::move(result_node)));
523-
break;
524-
case osmium::item_type::way:
525-
result_way.clear();
526-
if (local_context.has_way_function)
527-
{
528-
local_context.ProcessWay(static_cast<const osmium::Way &>(*entity),
529-
result_way);
530-
}
531-
resulting_ways.push_back(std::make_pair(x, std::move(result_way)));
532-
break;
533-
case osmium::item_type::relation:
534-
result_res.clear();
535-
result_res =
536-
restriction_parser.TryParse(static_cast<const osmium::Relation &>(*entity));
537-
for (const InputRestrictionContainer &r : result_res)
538-
{
539-
resulting_restrictions.push_back(r);
540-
}
541-
break;
542-
default:
543-
break;
544-
}
513+
local_context.ProcessNode(static_cast<const osmium::Node &>(*entity), result_node);
545514
}
546-
});
515+
resulting_nodes.push_back(std::pair<const osmium::Node &, ExtractionNode>(
516+
static_cast<const osmium::Node &>(*entity), std::move(result_node)));
517+
break;
518+
case osmium::item_type::way:
519+
result_way.clear();
520+
if (local_context.has_way_function)
521+
{
522+
local_context.ProcessWay(static_cast<const osmium::Way &>(*entity), result_way);
523+
}
524+
resulting_ways.push_back(std::pair<const osmium::Way &, ExtractionWay>(
525+
static_cast<const osmium::Way &>(*entity), std::move(result_way)));
526+
break;
527+
case osmium::item_type::relation:
528+
result_res.clear();
529+
result_res =
530+
restriction_parser.TryParse(static_cast<const osmium::Relation &>(*entity));
531+
for (const InputRestrictionContainer &r : result_res)
532+
{
533+
resulting_restrictions.push_back(r);
534+
}
535+
break;
536+
default:
537+
break;
538+
}
539+
}
547540
}
548541

549542
std::vector<std::string> Sol2ScriptingEnvironment::GetNameSuffixList()

0 commit comments

Comments
 (0)