Renumber nodes after running osrm-partition

The new numbering uses the partition information
to sort border nodes first to compactify storages
that need access indexed by border node ID.

We also get an optimized cache performance for free
sincr we can also recursively sort the nodes by cell ID.

This implements issue #3779.
This commit is contained in:
Patrick Niklaus
2017-05-19 22:28:01 +00:00
committed by Patrick Niklaus
parent a195d7dfd3
commit 0266c9d969
28 changed files with 975 additions and 241 deletions
+29 -11
View File
@@ -1,6 +1,7 @@
#ifndef DEALLOCATING_VECTOR_HPP
#define DEALLOCATING_VECTOR_HPP
#include "storage/io_fwd.hpp"
#include "util/integer_range.hpp"
#include <boost/iterator/iterator_facade.hpp>
@@ -13,7 +14,23 @@ namespace osrm
{
namespace util
{
template <typename ElementT> class DeallocatingVector;
}
namespace storage
{
namespace serialization
{
template <typename T>
inline void read(storage::io::FileReader &reader, util::DeallocatingVector<T> &vec);
template <typename T>
inline void write(storage::io::FileWriter &writer, const util::DeallocatingVector<T> &vec);
}
}
namespace util
{
template <typename ElementT> struct ConstDeallocatingVectorIteratorState
{
ConstDeallocatingVectorIteratorState()
@@ -216,18 +233,16 @@ class DeallocatingVectorRemoveIterator
}
};
template <typename ElementT, std::size_t ELEMENTS_PER_BLOCK> class DeallocatingVector;
template <typename T> void swap(DeallocatingVector<T> &lhs, DeallocatingVector<T> &rhs);
template <typename T, std::size_t S>
void swap(DeallocatingVector<T, S> &lhs, DeallocatingVector<T, S> &rhs);
template <typename ElementT, std::size_t ELEMENTS_PER_BLOCK = 8388608 / sizeof(ElementT)>
class DeallocatingVector
template <typename ElementT> class DeallocatingVector
{
static constexpr std::size_t ELEMENTS_PER_BLOCK = 8388608 / sizeof(ElementT);
std::size_t current_size;
std::vector<ElementT *> bucket_list;
public:
using value_type = ElementT;
using iterator = DeallocatingVectorIterator<ElementT, ELEMENTS_PER_BLOCK>;
using const_iterator = ConstDeallocatingVectorIterator<ElementT, ELEMENTS_PER_BLOCK>;
@@ -248,10 +263,9 @@ class DeallocatingVector
~DeallocatingVector() { clear(); }
friend void swap<>(DeallocatingVector<ElementT, ELEMENTS_PER_BLOCK> &lhs,
DeallocatingVector<ElementT, ELEMENTS_PER_BLOCK> &rhs);
friend void swap<>(DeallocatingVector<ElementT> &lhs, DeallocatingVector<ElementT> &rhs);
void swap(DeallocatingVector<ElementT, ELEMENTS_PER_BLOCK> &other)
void swap(DeallocatingVector<ElementT> &other)
{
std::swap(current_size, other.current_size);
bucket_list.swap(other.bucket_list);
@@ -377,10 +391,14 @@ class DeallocatingVector
++position;
}
}
friend void storage::serialization::read<ElementT>(storage::io::FileReader &reader,
DeallocatingVector &vec);
friend void storage::serialization::write<ElementT>(storage::io::FileWriter &writer,
const DeallocatingVector &vec);
};
template <typename T, std::size_t S>
void swap(DeallocatingVector<T, S> &lhs, DeallocatingVector<T, S> &rhs)
template <typename T> void swap(DeallocatingVector<T> &lhs, DeallocatingVector<T> &rhs)
{
lhs.swap(rhs);
}
+53
View File
@@ -3,6 +3,7 @@
#include "util/deallocating_vector.hpp"
#include "util/integer_range.hpp"
#include "util/permutation.hpp"
#include "util/typedefs.hpp"
#include "storage/io_fwd.hpp"
@@ -117,6 +118,28 @@ template <typename EdgeDataT> class DynamicGraph
}
}
DynamicGraph(DynamicGraph &&other)
{
number_of_nodes = other.number_of_nodes;
// atomics can't be moved this is why we need an own constructor
number_of_edges = static_cast<std::uint32_t>(other.number_of_edges);
node_array = std::move(other.node_array);
edge_list = std::move(other.edge_list);
}
DynamicGraph &operator=(DynamicGraph &&other)
{
number_of_nodes = other.number_of_nodes;
// atomics can't be moved this is why we need an own constructor
number_of_edges = static_cast<std::uint32_t>(other.number_of_edges);
node_array = std::move(other.node_array);
edge_list = std::move(other.edge_list);
return *this;
}
unsigned GetNumberOfNodes() const { return number_of_nodes; }
unsigned GetNumberOfEdges() const { return number_of_edges; }
@@ -309,6 +332,36 @@ template <typename EdgeDataT> class DynamicGraph
return current_iterator;
}
void Renumber(const std::vector<NodeID> &old_to_new_node)
{
// permutate everything but the sentinel
util::inplacePermutation(node_array.begin(), std::prev(node_array.end()), old_to_new_node);
// Build up edge permutation
auto new_edge_index = 0;
std::vector<EdgeID> old_to_new_edge(edge_list.size(), SPECIAL_EDGEID);
for (auto node : util::irange<NodeID>(0, number_of_nodes))
{
auto new_first_edge = new_edge_index;
// move all filled edges
for (auto edge : GetAdjacentEdgeRange(node))
{
edge_list[edge].target = old_to_new_node[edge_list[edge].target];
old_to_new_edge[edge] = new_edge_index++;
}
// and all adjacent empty edges
for (auto edge = EndEdges(node); edge < number_of_edges && isDummy(edge); edge++)
{
old_to_new_edge[edge] = new_edge_index++;
}
node_array[node].first_edge = new_first_edge;
}
BOOST_ASSERT(std::find(old_to_new_edge.begin(), old_to_new_edge.end(), SPECIAL_EDGEID) ==
old_to_new_edge.end());
util::inplacePermutation(edge_list.begin(), edge_list.end(), old_to_new_edge);
}
protected:
bool isDummy(const EdgeIterator edge) const
{
+54
View File
@@ -0,0 +1,54 @@
#ifndef OSRM_UTIL_MMAP_FILE_HPP
#define OSRM_UTIL_MMAP_FILE_HPP
#include "util/exception.hpp"
#include "util/exception_utils.hpp"
#include "util/vector_view.hpp"
#include <boost/filesystem/path.hpp>
#include <boost/iostreams/device/mapped_file.hpp>
namespace osrm
{
namespace util
{
namespace detail
{
template <typename T, typename RegionT>
util::vector_view<T> mmapFile(const boost::filesystem::path &file, RegionT &region)
{
try
{
region.open(file);
std::size_t num_objects = region.size() / sizeof(T);
auto data_ptr = region.data();
BOOST_ASSERT(reinterpret_cast<uintptr_t>(data_ptr) % alignof(T) == 0);
return util::vector_view<T>(reinterpret_cast<T *>(data_ptr), num_objects);
}
catch (const std::exception &exc)
{
throw exception(
boost::str(boost::format("File %1% mapping failed: %2%") % file % exc.what()) +
SOURCE_REF);
}
}
}
template <typename T>
util::vector_view<const T> mmapFile(const boost::filesystem::path &file,
boost::iostreams::mapped_file_source &region)
{
return detail::mmapFile<const T>(file, region);
}
template <typename T>
util::vector_view<T> mmapFile(const boost::filesystem::path &file,
boost::iostreams::mapped_file &region)
{
return detail::mmapFile<T>(file, region);
}
}
}
#endif
+44
View File
@@ -0,0 +1,44 @@
#ifndef OSRM_UTIL_PERMUTATION_HPP
#define OSRM_UTIL_PERMUTATION_HPP
#include "util/integer_range.hpp"
#include <vector>
namespace osrm
{
namespace util
{
template <typename RandomAccesIterator, typename IndexT>
void inplacePermutation(RandomAccesIterator begin,
RandomAccesIterator end,
const std::vector<IndexT> &old_to_new)
{
std::size_t size = std::distance(begin, end);
BOOST_ASSERT(old_to_new.size() == size);
// we need a little bit auxililary space since we need to mark
// replaced elements in a non-destructive way
std::vector<bool> was_replaced(size, false);
for (auto index : util::irange<IndexT>(0, size))
{
if (was_replaced[index])
continue;
// iterate over a cycle in the permutation
auto buffer = begin[index];
auto old_index = index;
auto new_index = old_to_new[old_index];
for (; new_index != index; old_index = new_index, new_index = old_to_new[new_index])
{
was_replaced[old_index] = true;
std::swap(buffer, begin[new_index]);
}
was_replaced[old_index] = true;
std::swap(buffer, begin[index]);
}
}
}
}
#endif
+2
View File
@@ -34,6 +34,8 @@ inline void read(storage::io::FileReader &reader, StaticGraph<EdgeDataT, Ownersh
{
storage::serialization::read(reader, graph.node_array);
storage::serialization::read(reader, graph.edge_array);
graph.number_of_nodes = graph.node_array.size() - 1;
graph.number_of_edges = graph.edge_array.size();
}
template <typename EdgeDataT, storage::Ownership Ownership>
+31
View File
@@ -4,6 +4,7 @@
#include "util/graph_traits.hpp"
#include "util/integer_range.hpp"
#include "util/percent.hpp"
#include "util/permutation.hpp"
#include "util/typedefs.hpp"
#include "util/vector_view.hpp"
@@ -146,6 +147,7 @@ class StaticGraph
number_of_nodes = static_cast<decltype(number_of_nodes)>(node_array.size() - 1);
number_of_edges = static_cast<decltype(number_of_edges)>(node_array.back().first_edge);
BOOST_ASSERT(number_of_edges <= edge_array.size());
BOOST_ASSERT(number_of_nodes == node_array.size() - 1);
}
unsigned GetNumberOfNodes() const { return number_of_nodes; }
@@ -241,6 +243,35 @@ class StaticGraph
return current_iterator;
}
void Renumber(const std::vector<NodeID> &old_to_new_node)
{
std::vector<NodeID> new_to_old_node(number_of_nodes);
for (auto node : util::irange<NodeID>(0, number_of_nodes))
new_to_old_node[old_to_new_node[node]] = node;
Vector<NodeArrayEntry> new_node_array(node_array.size());
// Build up edge permutation
auto new_edge_index = 0;
std::vector<EdgeID> old_to_new_edge(edge_array.size(), SPECIAL_EDGEID);
for (auto node : util::irange<NodeID>(0, number_of_nodes))
{
auto new_first_edge = new_edge_index;
for (auto edge : GetAdjacentEdgeRange(new_to_old_node[node]))
{
edge_array[edge].target = old_to_new_node[edge_array[edge].target];
old_to_new_edge[edge] = new_edge_index++;
}
new_node_array[node].first_edge = new_first_edge;
}
new_node_array.back().first_edge = new_edge_index;
node_array = std::move(new_node_array);
BOOST_ASSERT(std::find(old_to_new_edge.begin(), old_to_new_edge.end(), SPECIAL_EDGEID) ==
old_to_new_edge.end());
util::inplacePermutation(edge_array.begin(), edge_array.end(), old_to_new_edge);
}
friend void serialization::read<EdgeDataT, Ownership>(storage::io::FileReader &reader,
StaticGraph<EdgeDataT, Ownership> &graph);
friend void
+5 -38
View File
@@ -8,6 +8,7 @@
#include "util/exception.hpp"
#include "util/hilbert_value.hpp"
#include "util/integer_range.hpp"
#include "util/mmap_file.hpp"
#include "util/rectangle.hpp"
#include "util/typedefs.hpp"
#include "util/vector_view.hpp"
@@ -456,9 +457,8 @@ class StaticRTree
tree_node_file.WriteOne(static_cast<std::uint64_t>(m_tree_level_sizes.size()));
tree_node_file.WriteFrom(m_tree_level_sizes);
}
// Map the leaf nodes file so that the r-tree object is immediately usable (i.e. the
// constructor doesn't just build and serialize the tree, it gives us a usable r-tree).
MapLeafNodesFile(leaf_node_filename);
m_objects = mmapFile<EdgeDataT>(leaf_node_filename, m_objects_region);
}
/**
@@ -488,7 +488,7 @@ class StaticRTree
m_tree_level_sizes.end() - 1,
std::back_inserter(m_tree_level_starts));
MapLeafNodesFile(leaf_file);
m_objects = mmapFile<EdgeDataT>(leaf_file, m_objects_region);
}
/**
@@ -512,40 +512,7 @@ class StaticRTree
std::partial_sum(m_tree_level_sizes.begin(),
m_tree_level_sizes.end() - 1,
std::back_inserter(m_tree_level_starts));
MapLeafNodesFile(leaf_file);
}
/**
* mmap()s the .fileIndex file and wrapps it in a read-only vector_view object
* for easy access.
*/
void MapLeafNodesFile(const boost::filesystem::path &leaf_file)
{
// open leaf node file and return a pointer to the mapped leaves data
try
{
m_objects_region.open(leaf_file);
std::size_t num_objects = m_objects_region.size() / sizeof(EdgeDataT);
auto data_ptr = m_objects_region.data();
BOOST_ASSERT(reinterpret_cast<uintptr_t>(data_ptr) % alignof(EdgeDataT) == 0);
BOOST_ASSERT(m_search_tree.size() > 0);
BOOST_ASSERT(m_tree_level_sizes.size() > 0);
// Verify that there are at least enough objects to fill the bottom of the leaf nods
// This is a rough check for correct file length. It's not strictly correct, it
// misses the last LEAF_NODE_SIZE-1 nodes, but it should generally be good enough
// to catch most problems. The second test is for when the m_objects array is perfectly
// filled and has a size that is dividable by LEAF_NODE_SIZE without a remainder
BOOST_ASSERT(m_tree_level_sizes.back() - 1 ==
std::floor(num_objects / LEAF_NODE_SIZE) ||
m_tree_level_sizes.back() == std::floor(num_objects / LEAF_NODE_SIZE));
m_objects.reset(reinterpret_cast<const EdgeDataT *>(data_ptr), num_objects);
}
catch (const std::exception &exc)
{
throw exception(boost::str(boost::format("Leaf file %1% mapping failed: %2%") %
leaf_file % exc.what()) +
SOURCE_REF);
}
m_objects = mmapFile<EdgeDataT>(leaf_file, m_objects_region);
}
/* Returns all features inside the bounding box.
+94
View File
@@ -0,0 +1,94 @@
#ifndef OSRM_UTIL_TIMED_HISTOGRAM_HPP
#define OSRM_UTIL_TIMED_HISTOGRAM_HPP
#include "util/integer_range.hpp"
#include <algorithm>
#include <atomic>
#include <mutex>
#include <sstream>
#include <vector>
namespace osrm
{
namespace util
{
namespace detail
{
extern std::atomic_uint operation;
}
/**
* Captures a histogram with a bin size of `IndexBinSize` every `TimeBinSize` count operations.
*/
template <std::size_t TimeBinSize = 1000, std::size_t IndexBinSize = 1000> class TimedHistogram
{
public:
void Count(std::size_t pos)
{
std::lock_guard<std::mutex> guard(frames_lock);
auto frame_index = detail::operation++ / TimeBinSize;
while (frame_offsets.size() <= frame_index)
{
frame_offsets.push_back(frame_counters.size());
}
BOOST_ASSERT(frame_offsets.size() == frame_index + 1);
auto frame_offset = frame_offsets.back();
auto counter_index = frame_offset + pos / IndexBinSize;
while (counter_index >= frame_counters.size())
{
frame_counters.push_back(0);
}
BOOST_ASSERT(frame_counters.size() > counter_index);
frame_counters[counter_index]++;
}
// Returns the measurments as a CSV file with the columns:
// frame_id,index_bin,count
std::string DumpCSV() const
{
std::stringstream out;
const auto print_bins = [&out](auto frame_index, auto begin, auto end) {
auto bin_index = 0;
std::for_each(begin, end, [&](const auto count) {
if (count > 0)
{
out << (frame_index * TimeBinSize) << "," << (bin_index * IndexBinSize) << ","
<< count << std::endl;
}
bin_index++;
});
};
if (frame_offsets.size() == 0)
{
return "";
}
for (const auto frame_index : irange<std::size_t>(0, frame_offsets.size() - 1))
{
auto begin = frame_counters.begin() + frame_offsets[frame_index];
auto end = frame_counters.begin() + frame_offsets[frame_index + 1];
print_bins(frame_index, begin, end);
}
print_bins(frame_offsets.size() - 1,
frame_counters.begin() + frame_offsets.back(),
frame_counters.end());
return out.str();
}
private:
std::mutex frames_lock;
std::vector<std::uint32_t> frame_offsets;
std::vector<std::uint32_t> frame_counters;
};
}
}
#endif