Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

shard the node store to reduce memory usage #571

Merged
merged 2 commits into from
Nov 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions include/geom.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ typedef boost::variant<Point,Linestring,MultiLinestring,MultiPolygon> Geometry;
typedef std::pair<Box, uint> IndexValue;
typedef boost::geometry::index::rtree< IndexValue, boost::geometry::index::quadratic<16> > RTree;

// A 36-bit integer can store all OSM node IDs; we represent this as 16 collections
// of 32-bit integers.
#define NODE_SHARDS 16
typedef uint32_t ShardedNodeID;
typedef uint64_t NodeID;
typedef uint64_t WayID;

Expand Down
76 changes: 51 additions & 25 deletions include/osm_store.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,24 +79,34 @@ class NodeStore

public:
using element_t = std::pair<NodeID, LatpLon>;
using map_t = std::deque<element_t, mmap_allocator<element_t>>;
using internal_element_t = std::pair<ShardedNodeID, LatpLon>;
using map_t = std::deque<internal_element_t, mmap_allocator<internal_element_t>>;

void reopen()
{
std::lock_guard<std::mutex> lock(mutex);
mLatpLons = std::make_unique<map_t>();
for (auto i = 0; i < mLatpLons.size(); i++)
mLatpLons[i]->clear();

mLatpLons.clear();
for (auto i = 0; i < NODE_SHARDS; i++) {
mLatpLons.push_back(std::make_unique<map_t>());
}
}

// @brief Lookup a latp/lon pair
// @param i OSM ID of a node
// @return Latp/lon pair
// @exception NotFound
LatpLon at(NodeID i) const {
auto iter = std::lower_bound(mLatpLons->begin(), mLatpLons->end(), i, [](auto const &e, auto i) {
auto shard = mLatpLons[shardPart(i)];
auto id = idPart(i);

auto iter = std::lower_bound(shard->begin(), shard->end(), id, [](auto const &e, auto i) {
return e.first < i;
});

if(iter == mLatpLons->end() || iter->first != i)
if(iter == shard->end() || iter->first != id)
throw std::out_of_range("Could not find node with id " + std::to_string(i));

return iter->second;
Expand All @@ -105,36 +115,58 @@ class NodeStore
// @brief Return the number of stored items
size_t size() const {
std::lock_guard<std::mutex> lock(mutex);
return mLatpLons->size();
}
uint64_t size = 0;
for (auto i = 0; i < mLatpLons.size(); i++)
size += mLatpLons[i]->size();

// @brief Insert a latp/lon pair.
// @param i OSM ID of a node
// @param coord a latp/lon pair to be inserted
// @invariant The OSM ID i must be larger than previously inserted OSM IDs of nodes
// (though unnecessarily for current impl, future impl may impose that)
void insert_back(NodeID i, LatpLon coord) {
mLatpLons->push_back(std::make_pair(i, coord));
return size;
}

void insert_back(std::vector<element_t> const &element) {
uint32_t newEntries[NODE_SHARDS] = {};
std::vector<map_t::iterator> iterators;

// Before taking the lock, do a pass to find out how much
// to grow each backing collection
for (auto it = element.begin(); it != element.end(); it++) {
newEntries[shardPart(it->first)]++;
}

std::lock_guard<std::mutex> lock(mutex);
auto i = mLatpLons->size();
mLatpLons->resize(i + element.size());
std::copy(element.begin(), element.end(), mLatpLons->begin() + i);
for (auto i = 0; i < NODE_SHARDS; i++) {
auto size = mLatpLons[i]->size();
mLatpLons[i]->resize(size + newEntries[i]);
iterators.push_back(mLatpLons[i]->begin() + size);
}

for (auto it = element.begin(); it != element.end(); it++) {
uint32_t shard = shardPart(it->first);
uint32_t id = idPart(it->first);

*iterators[shard] = std::make_pair(id, it->second);
iterators[shard]++;
}
}

// @brief Make the store empty
void clear() {
std::lock_guard<std::mutex> lock(mutex);
mLatpLons->clear();
reopen();
}

void sort(unsigned int threadNum);

private:
mutable std::mutex mutex;
std::shared_ptr<map_t> mLatpLons;
std::vector<std::shared_ptr<map_t>> mLatpLons;

uint32_t shardPart(NodeID id) const {
uint32_t rv = id >> 32;
return rv;
}

uint32_t idPart(NodeID id) const {
return id;
}
};

class CompactNodeStore
Expand Down Expand Up @@ -469,12 +501,6 @@ class OSMStore
void shapes_sort(unsigned int threadNum = 1);
void generated_sort(unsigned int threadNum = 1);

void nodes_insert_back(NodeID i, LatpLon coord) {
if(!use_compact_nodes)
nodes.insert_back(i, coord);
else
compact_nodes.insert_back(i, coord);
}
void nodes_insert_back(std::vector<NodeStore::element_t> const &new_nodes) {
if(!use_compact_nodes)
nodes.insert_back(new_nodes);
Expand Down
10 changes: 6 additions & 4 deletions src/osm_store.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -262,10 +262,12 @@ void void_mmap_allocator::destroy(void *p)

void NodeStore::sort(unsigned int threadNum) {
std::lock_guard<std::mutex> lock(mutex);
boost::sort::block_indirect_sort(
mLatpLons->begin(), mLatpLons->end(),
[](auto const &a, auto const &b) { return a.first < b.first; },
threadNum);
for (auto i = 0; i < NODE_SHARDS; i++) {
boost::sort::block_indirect_sort(
mLatpLons[i]->begin(), mLatpLons[i]->end(),
[](auto const &a, auto const &b) { return a.first < b.first; },
threadNum);
}
}

void WayStore::sort(unsigned int threadNum) {
Expand Down
Loading