Use ArrayStorage for boundary nodes to optimize MLD
For the MLD algorithm we can partition the NodeID range into boundary and non-boundary nodes. Since there are only we boundary nodes we can use the ArrayStorage for those yielding much faster query times.
This commit is contained in:
parent
7edf0f218c
commit
bf2b45120a
@ -65,6 +65,8 @@ template <> class AlgorithmDataFacade<MLD>
|
|||||||
// search graph access
|
// search graph access
|
||||||
virtual unsigned GetNumberOfNodes() const = 0;
|
virtual unsigned GetNumberOfNodes() const = 0;
|
||||||
|
|
||||||
|
virtual unsigned GetMaxBorderNodeID() const = 0;
|
||||||
|
|
||||||
virtual unsigned GetNumberOfEdges() const = 0;
|
virtual unsigned GetNumberOfEdges() const = 0;
|
||||||
|
|
||||||
virtual unsigned GetOutDegree(const NodeID n) const = 0;
|
virtual unsigned GetOutDegree(const NodeID n) const = 0;
|
||||||
|
@ -675,6 +675,8 @@ template <> class ContiguousInternalMemoryAlgorithmDataFacade<MLD> : public Algo
|
|||||||
// search graph access
|
// search graph access
|
||||||
unsigned GetNumberOfNodes() const override final { return query_graph.GetNumberOfNodes(); }
|
unsigned GetNumberOfNodes() const override final { return query_graph.GetNumberOfNodes(); }
|
||||||
|
|
||||||
|
unsigned GetMaxBorderNodeID() const override final { return query_graph.GetMaxBorderNodeID(); }
|
||||||
|
|
||||||
unsigned GetNumberOfEdges() const override final { return query_graph.GetNumberOfEdges(); }
|
unsigned GetNumberOfEdges() const override final { return query_graph.GetNumberOfEdges(); }
|
||||||
|
|
||||||
unsigned GetOutDegree(const NodeID n) const override final
|
unsigned GetOutDegree(const NodeID n) const override final
|
||||||
|
@ -207,6 +207,25 @@ void unpackLegs(const DataFacade<Algorithm> &facade,
|
|||||||
phantom_nodes_vector[current_leg].target_phantom.forward_segment_id.id));
|
phantom_nodes_vector[current_leg].target_phantom.forward_segment_id.id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Algorithm>
|
||||||
|
inline void initializeHeap(SearchEngineData<Algorithm> &engine_working_data,
|
||||||
|
const DataFacade<Algorithm> &facade)
|
||||||
|
{
|
||||||
|
|
||||||
|
const auto nodes_number = facade.GetNumberOfNodes();
|
||||||
|
engine_working_data.InitializeOrClearFirstThreadLocalStorage(nodes_number);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline void initializeHeap<mld::Algorithm>(SearchEngineData<mld::Algorithm> &engine_working_data,
|
||||||
|
const DataFacade<mld::Algorithm> &facade)
|
||||||
|
{
|
||||||
|
|
||||||
|
const auto nodes_number = facade.GetNumberOfNodes();
|
||||||
|
const auto border_nodes_number = facade.GetMaxBorderNodeID() + 1;
|
||||||
|
engine_working_data.InitializeOrClearFirstThreadLocalStorage(nodes_number, border_nodes_number);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Algorithm>
|
template <typename Algorithm>
|
||||||
@ -221,7 +240,7 @@ InternalRouteResult shortestPathSearch(SearchEngineData<Algorithm> &engine_worki
|
|||||||
!(continue_straight_at_waypoint ? *continue_straight_at_waypoint
|
!(continue_straight_at_waypoint ? *continue_straight_at_waypoint
|
||||||
: facade.GetContinueStraightDefault());
|
: facade.GetContinueStraightDefault());
|
||||||
|
|
||||||
engine_working_data.InitializeOrClearFirstThreadLocalStorage(facade.GetNumberOfNodes());
|
initializeHeap(engine_working_data, facade);
|
||||||
|
|
||||||
auto &forward_heap = *engine_working_data.forward_heap_1;
|
auto &forward_heap = *engine_working_data.forward_heap_1;
|
||||||
auto &reverse_heap = *engine_working_data.reverse_heap_1;
|
auto &reverse_heap = *engine_working_data.reverse_heap_1;
|
||||||
|
@ -91,13 +91,13 @@ template <> struct SearchEngineData<routing_algorithms::mld::Algorithm>
|
|||||||
NodeID,
|
NodeID,
|
||||||
EdgeWeight,
|
EdgeWeight,
|
||||||
MultiLayerDijkstraHeapData,
|
MultiLayerDijkstraHeapData,
|
||||||
util::UnorderedMapStorage<NodeID, int>>;
|
util::TwoLevelStorage<NodeID, int>>;
|
||||||
|
|
||||||
using ManyToManyQueryHeap = util::QueryHeap<NodeID,
|
using ManyToManyQueryHeap = util::QueryHeap<NodeID,
|
||||||
NodeID,
|
NodeID,
|
||||||
EdgeWeight,
|
EdgeWeight,
|
||||||
ManyToManyMultiLayerDijkstraHeapData,
|
ManyToManyMultiLayerDijkstraHeapData,
|
||||||
util::UnorderedMapStorage<NodeID, int>>;
|
util::TwoLevelStorage<NodeID, int>>;
|
||||||
|
|
||||||
using SearchEngineHeapPtr = boost::thread_specific_ptr<QueryHeap>;
|
using SearchEngineHeapPtr = boost::thread_specific_ptr<QueryHeap>;
|
||||||
using ManyToManyHeapPtr = boost::thread_specific_ptr<ManyToManyQueryHeap>;
|
using ManyToManyHeapPtr = boost::thread_specific_ptr<ManyToManyQueryHeap>;
|
||||||
@ -106,9 +106,11 @@ template <> struct SearchEngineData<routing_algorithms::mld::Algorithm>
|
|||||||
static SearchEngineHeapPtr reverse_heap_1;
|
static SearchEngineHeapPtr reverse_heap_1;
|
||||||
static ManyToManyHeapPtr many_to_many_heap;
|
static ManyToManyHeapPtr many_to_many_heap;
|
||||||
|
|
||||||
void InitializeOrClearFirstThreadLocalStorage(unsigned number_of_nodes);
|
void InitializeOrClearFirstThreadLocalStorage(unsigned number_of_nodes,
|
||||||
|
unsigned number_of_boundary_nodes);
|
||||||
|
|
||||||
void InitializeOrClearManyToManyThreadLocalStorage(unsigned number_of_nodes);
|
void InitializeOrClearManyToManyThreadLocalStorage(unsigned number_of_nodes,
|
||||||
|
unsigned number_of_boundary_nodes);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,6 +138,14 @@ class MultiLevelGraph : public util::StaticGraph<EdgeDataT, Ownership>
|
|||||||
// We save the level as sentinel at the end
|
// We save the level as sentinel at the end
|
||||||
LevelID GetNumberOfLevels() const { return node_to_edge_offset.back(); }
|
LevelID GetNumberOfLevels() const { return node_to_edge_offset.back(); }
|
||||||
|
|
||||||
|
NodeID GetMaxBorderNodeID() const
|
||||||
|
{
|
||||||
|
auto num_levels = GetNumberOfLevels();
|
||||||
|
BOOST_ASSERT((node_to_edge_offset.size() - 1) % num_levels == 0);
|
||||||
|
auto max_border_node_id = (node_to_edge_offset.size() - 1) / num_levels - 1;
|
||||||
|
return max_border_node_id;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template <typename ContainerT>
|
template <typename ContainerT>
|
||||||
auto GetHighestBorderLevel(const MultiLevelPartition &mlp, const ContainerT &edges) const
|
auto GetHighestBorderLevel(const MultiLevelPartition &mlp, const ContainerT &edges) const
|
||||||
|
@ -20,7 +20,7 @@ template <typename NodeID, typename Key> class GenerationArrayStorage
|
|||||||
using GenerationCounter = std::uint16_t;
|
using GenerationCounter = std::uint16_t;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit GenerationArrayStorage(std::size_t size)
|
explicit GenerationArrayStorage(std::size_t size, std::size_t)
|
||||||
: positions(size, 0), generation(1), generations(size, 0)
|
: positions(size, 0), generation(1), generations(size, 0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -60,7 +60,7 @@ template <typename NodeID, typename Key> class GenerationArrayStorage
|
|||||||
template <typename NodeID, typename Key> class ArrayStorage
|
template <typename NodeID, typename Key> class ArrayStorage
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit ArrayStorage(std::size_t size) : positions(size, 0) {}
|
explicit ArrayStorage(std::size_t size, std::size_t) : positions(size, 0) {}
|
||||||
|
|
||||||
~ArrayStorage() {}
|
~ArrayStorage() {}
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ template <typename NodeID, typename Key> class ArrayStorage
|
|||||||
template <typename NodeID, typename Key> class MapStorage
|
template <typename NodeID, typename Key> class MapStorage
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit MapStorage(std::size_t) {}
|
explicit MapStorage(std::size_t, std::size_t) {}
|
||||||
|
|
||||||
Key &operator[](NodeID node) { return nodes[node]; }
|
Key &operator[](NodeID node) { return nodes[node]; }
|
||||||
|
|
||||||
@ -100,7 +100,7 @@ template <typename NodeID, typename Key> class MapStorage
|
|||||||
template <typename NodeID, typename Key> class UnorderedMapStorage
|
template <typename NodeID, typename Key> class UnorderedMapStorage
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit UnorderedMapStorage(std::size_t) { nodes.rehash(1000); }
|
explicit UnorderedMapStorage(std::size_t, std::size_t) { nodes.rehash(1000); }
|
||||||
|
|
||||||
Key &operator[](const NodeID node) { return nodes[node]; }
|
Key &operator[](const NodeID node) { return nodes[node]; }
|
||||||
|
|
||||||
@ -126,6 +126,67 @@ template <typename NodeID, typename Key> class UnorderedMapStorage
|
|||||||
std::unordered_map<NodeID, Key> nodes;
|
std::unordered_map<NodeID, Key> nodes;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename NodeID,
|
||||||
|
typename Key,
|
||||||
|
template <typename N, typename K> class BaseIndexStorage = UnorderedMapStorage,
|
||||||
|
template <typename N, typename K> class OverlayIndexStorage = ArrayStorage>
|
||||||
|
class TwoLevelStorage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit TwoLevelStorage(std::size_t number_of_nodes, std::size_t number_of_overlay_nodes)
|
||||||
|
: number_of_overlay_nodes(number_of_overlay_nodes), base(number_of_nodes, number_of_nodes),
|
||||||
|
overlay(number_of_overlay_nodes, number_of_overlay_nodes)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Key &operator[](const NodeID node)
|
||||||
|
{
|
||||||
|
if (node < number_of_overlay_nodes)
|
||||||
|
{
|
||||||
|
return overlay[node];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return base[node];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Key peek_index(const NodeID node) const
|
||||||
|
{
|
||||||
|
if (node < number_of_overlay_nodes)
|
||||||
|
{
|
||||||
|
return overlay.peek_index(node);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return base.peek_index(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Key const &operator[](const NodeID node) const
|
||||||
|
{
|
||||||
|
if (node < number_of_overlay_nodes)
|
||||||
|
{
|
||||||
|
return overlay[node];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return base[node];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Clear()
|
||||||
|
{
|
||||||
|
base.Clear();
|
||||||
|
overlay.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::size_t number_of_overlay_nodes;
|
||||||
|
BaseIndexStorage<NodeID, Key> base;
|
||||||
|
OverlayIndexStorage<NodeID, Key> overlay;
|
||||||
|
};
|
||||||
|
|
||||||
template <typename NodeID,
|
template <typename NodeID,
|
||||||
typename Key,
|
typename Key,
|
||||||
typename Weight,
|
typename Weight,
|
||||||
@ -137,7 +198,16 @@ class QueryHeap
|
|||||||
using WeightType = Weight;
|
using WeightType = Weight;
|
||||||
using DataType = Data;
|
using DataType = Data;
|
||||||
|
|
||||||
explicit QueryHeap(std::size_t maxID) : node_index(maxID) { Clear(); }
|
explicit QueryHeap(std::size_t number_of_elements, std::size_t number_of_overlay_nodes)
|
||||||
|
: node_index(number_of_elements, number_of_overlay_nodes)
|
||||||
|
{
|
||||||
|
Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit QueryHeap(std::size_t number_of_elements)
|
||||||
|
: QueryHeap(number_of_elements, number_of_elements)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void Clear()
|
void Clear()
|
||||||
{
|
{
|
||||||
|
@ -31,7 +31,9 @@ class XORFastHashStorage
|
|||||||
void operator=(const Key key_to_insert) { key = key_to_insert; }
|
void operator=(const Key key_to_insert) { key = key_to_insert; }
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit XORFastHashStorage(size_t) : positions(MaxNumElements), current_timestamp{0u} {}
|
explicit XORFastHashStorage(size_t, size_t) : positions(MaxNumElements), current_timestamp{0u}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
HashCell &operator[](const NodeID node)
|
HashCell &operator[](const NodeID node)
|
||||||
{
|
{
|
||||||
|
@ -700,7 +700,8 @@ InternalManyRoutesResult alternativePathSearch(SearchEngineData<Algorithm> &sear
|
|||||||
const Partition &partition = facade.GetMultiLevelPartition();
|
const Partition &partition = facade.GetMultiLevelPartition();
|
||||||
|
|
||||||
// Prepare heaps for usage below. The searches will modify them in-place.
|
// Prepare heaps for usage below. The searches will modify them in-place.
|
||||||
search_engine_data.InitializeOrClearFirstThreadLocalStorage(facade.GetNumberOfNodes());
|
search_engine_data.InitializeOrClearFirstThreadLocalStorage(facade.GetNumberOfNodes(),
|
||||||
|
facade.GetMaxBorderNodeID() + 1);
|
||||||
|
|
||||||
Heap &forward_heap = *search_engine_data.forward_heap_1;
|
Heap &forward_heap = *search_engine_data.forward_heap_1;
|
||||||
Heap &reverse_heap = *search_engine_data.reverse_heap_1;
|
Heap &reverse_heap = *search_engine_data.reverse_heap_1;
|
||||||
|
@ -69,7 +69,8 @@ InternalRouteResult directShortestPathSearch(SearchEngineData<mld::Algorithm> &e
|
|||||||
const DataFacade<mld::Algorithm> &facade,
|
const DataFacade<mld::Algorithm> &facade,
|
||||||
const PhantomNodes &phantom_nodes)
|
const PhantomNodes &phantom_nodes)
|
||||||
{
|
{
|
||||||
engine_working_data.InitializeOrClearFirstThreadLocalStorage(facade.GetNumberOfNodes());
|
engine_working_data.InitializeOrClearFirstThreadLocalStorage(facade.GetNumberOfNodes(),
|
||||||
|
facade.GetMaxBorderNodeID() + 1);
|
||||||
auto &forward_heap = *engine_working_data.forward_heap_1;
|
auto &forward_heap = *engine_working_data.forward_heap_1;
|
||||||
auto &reverse_heap = *engine_working_data.reverse_heap_1;
|
auto &reverse_heap = *engine_working_data.reverse_heap_1;
|
||||||
insertNodesInHeaps(forward_heap, reverse_heap, phantom_nodes);
|
insertNodesInHeaps(forward_heap, reverse_heap, phantom_nodes);
|
||||||
|
@ -258,7 +258,8 @@ std::vector<EdgeDuration> oneToManySearch(SearchEngineData<Algorithm> &engine_wo
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialize query heap
|
// Initialize query heap
|
||||||
engine_working_data.InitializeOrClearManyToManyThreadLocalStorage(facade.GetNumberOfNodes());
|
engine_working_data.InitializeOrClearManyToManyThreadLocalStorage(
|
||||||
|
facade.GetNumberOfNodes(), facade.GetMaxBorderNodeID() + 1);
|
||||||
auto &query_heap = *(engine_working_data.many_to_many_heap);
|
auto &query_heap = *(engine_working_data.many_to_many_heap);
|
||||||
|
|
||||||
// Check if node is in the destinations list and update weights/durations
|
// Check if node is in the destinations list and update weights/durations
|
||||||
@ -465,7 +466,7 @@ std::vector<EdgeDuration> manyToManySearch(SearchEngineData<Algorithm> &engine_w
|
|||||||
const auto &phantom = phantom_nodes[index];
|
const auto &phantom = phantom_nodes[index];
|
||||||
|
|
||||||
engine_working_data.InitializeOrClearManyToManyThreadLocalStorage(
|
engine_working_data.InitializeOrClearManyToManyThreadLocalStorage(
|
||||||
facade.GetNumberOfNodes());
|
facade.GetNumberOfNodes(), facade.GetMaxBorderNodeID() + 1);
|
||||||
auto &query_heap = *(engine_working_data.many_to_many_heap);
|
auto &query_heap = *(engine_working_data.many_to_many_heap);
|
||||||
|
|
||||||
if (DIRECTION == FORWARD_DIRECTION)
|
if (DIRECTION == FORWARD_DIRECTION)
|
||||||
@ -492,7 +493,7 @@ std::vector<EdgeDuration> manyToManySearch(SearchEngineData<Algorithm> &engine_w
|
|||||||
|
|
||||||
// Clear heap and insert source nodes
|
// Clear heap and insert source nodes
|
||||||
engine_working_data.InitializeOrClearManyToManyThreadLocalStorage(
|
engine_working_data.InitializeOrClearManyToManyThreadLocalStorage(
|
||||||
facade.GetNumberOfNodes());
|
facade.GetNumberOfNodes(), facade.GetMaxBorderNodeID() + 1);
|
||||||
auto &query_heap = *(engine_working_data.many_to_many_heap);
|
auto &query_heap = *(engine_working_data.many_to_many_heap);
|
||||||
|
|
||||||
if (DIRECTION == FORWARD_DIRECTION)
|
if (DIRECTION == FORWARD_DIRECTION)
|
||||||
|
@ -46,6 +46,25 @@ unsigned getMedianSampleTime(const std::vector<unsigned> ×tamps)
|
|||||||
std::nth_element(first_elem, median, sample_times.end());
|
std::nth_element(first_elem, median, sample_times.end());
|
||||||
return *median;
|
return *median;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Algorithm>
|
||||||
|
inline void initializeHeap(SearchEngineData<Algorithm> &engine_working_data,
|
||||||
|
const DataFacade<Algorithm> &facade)
|
||||||
|
{
|
||||||
|
|
||||||
|
const auto nodes_number = facade.GetNumberOfNodes();
|
||||||
|
engine_working_data.InitializeOrClearFirstThreadLocalStorage(nodes_number);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline void initializeHeap<mld::Algorithm>(SearchEngineData<mld::Algorithm> &engine_working_data,
|
||||||
|
const DataFacade<mld::Algorithm> &facade)
|
||||||
|
{
|
||||||
|
|
||||||
|
const auto nodes_number = facade.GetNumberOfNodes();
|
||||||
|
const auto border_nodes_number = facade.GetMaxBorderNodeID() + 1;
|
||||||
|
engine_working_data.InitializeOrClearFirstThreadLocalStorage(nodes_number, border_nodes_number);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Algorithm>
|
template <typename Algorithm>
|
||||||
@ -131,9 +150,7 @@ SubMatchingList mapMatching(SearchEngineData<Algorithm> &engine_working_data,
|
|||||||
return sub_matchings;
|
return sub_matchings;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto nodes_number = facade.GetNumberOfNodes();
|
initializeHeap(engine_working_data, facade);
|
||||||
engine_working_data.InitializeOrClearFirstThreadLocalStorage(nodes_number);
|
|
||||||
|
|
||||||
auto &forward_heap = *engine_working_data.forward_heap_1;
|
auto &forward_heap = *engine_working_data.forward_heap_1;
|
||||||
auto &reverse_heap = *engine_working_data.reverse_heap_1;
|
auto &reverse_heap = *engine_working_data.reverse_heap_1;
|
||||||
|
|
||||||
|
@ -96,7 +96,8 @@ SearchEngineData<MLD>::SearchEngineHeapPtr SearchEngineData<MLD>::forward_heap_1
|
|||||||
SearchEngineData<MLD>::SearchEngineHeapPtr SearchEngineData<MLD>::reverse_heap_1;
|
SearchEngineData<MLD>::SearchEngineHeapPtr SearchEngineData<MLD>::reverse_heap_1;
|
||||||
SearchEngineData<MLD>::ManyToManyHeapPtr SearchEngineData<MLD>::many_to_many_heap;
|
SearchEngineData<MLD>::ManyToManyHeapPtr SearchEngineData<MLD>::many_to_many_heap;
|
||||||
|
|
||||||
void SearchEngineData<MLD>::InitializeOrClearFirstThreadLocalStorage(unsigned number_of_nodes)
|
void SearchEngineData<MLD>::InitializeOrClearFirstThreadLocalStorage(
|
||||||
|
unsigned number_of_nodes, unsigned number_of_boundary_nodes)
|
||||||
{
|
{
|
||||||
if (forward_heap_1.get())
|
if (forward_heap_1.get())
|
||||||
{
|
{
|
||||||
@ -104,7 +105,7 @@ void SearchEngineData<MLD>::InitializeOrClearFirstThreadLocalStorage(unsigned nu
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
forward_heap_1.reset(new QueryHeap(number_of_nodes));
|
forward_heap_1.reset(new QueryHeap(number_of_nodes, number_of_boundary_nodes));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reverse_heap_1.get())
|
if (reverse_heap_1.get())
|
||||||
@ -113,11 +114,12 @@ void SearchEngineData<MLD>::InitializeOrClearFirstThreadLocalStorage(unsigned nu
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
reverse_heap_1.reset(new QueryHeap(number_of_nodes));
|
reverse_heap_1.reset(new QueryHeap(number_of_nodes, number_of_boundary_nodes));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SearchEngineData<MLD>::InitializeOrClearManyToManyThreadLocalStorage(unsigned number_of_nodes)
|
void SearchEngineData<MLD>::InitializeOrClearManyToManyThreadLocalStorage(
|
||||||
|
unsigned number_of_nodes, unsigned number_of_boundary_nodes)
|
||||||
{
|
{
|
||||||
if (many_to_many_heap.get())
|
if (many_to_many_heap.get())
|
||||||
{
|
{
|
||||||
@ -125,7 +127,7 @@ void SearchEngineData<MLD>::InitializeOrClearManyToManyThreadLocalStorage(unsign
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
many_to_many_heap.reset(new ManyToManyQueryHeap(number_of_nodes));
|
many_to_many_heap.reset(new ManyToManyQueryHeap(number_of_nodes, number_of_boundary_nodes));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user