Files
tilemaker/include/attribute_store.h
Wouter van Kleunen a9a54f97ee Several optimization to reduce outputobject size (#307)
* Pack OutputObject bitfields more efficiently

* Move to plain enum to keep GCC happy

* Convert int40 to string

* Cast bitfield as a full int for logging

* Make enum names unique, because Windows

* Fake relation id shouldn't overtop the bitfield

* Pack into 36 bytes

* Improved simplify performance (#279)

* Remove reference counted outputobject

* Use OsmID to lookup generated geometry

* Remove temporary when assigning multipolygon

* Make attribute store ref an intrusive pointer

Co-authored-by: systemed <richard@systemeD.net>
2021-09-10 12:25:50 +01:00

147 lines
4.4 KiB
C++

/*! \file */
#ifndef _ATTRIBUTE_STORE_H
#define _ATTRIBUTE_STORE_H
#include "vector_tile.pb.h"
#include <boost/functional/hash.hpp>
#include <boost/intrusive_ptr.hpp>
#include <atomic>
/* AttributeStore
* global dictionaries for attributes
* We combine all similair keys/values pairs and sets
* - All the same key/value pairs are combined in key_values
* - All the same set of key/values are combined in set_list
*
* Every key/value set gets an ID in set_list. If the ID of two attribute sets are the same
* this means these objects share the same set of attribute/values. Output objects store a
* reference to the set of paremeters in set_list.
*/
struct AttributeStore
{
struct kv_with_minzoom {
std::string key;
vector_tile::Tile_Value value;
char minzoom;
kv_with_minzoom(std::string const &key, vector_tile::Tile_Value const &value, char minzoom)
: key(key), value(value), minzoom(minzoom)
{ }
};
enum class Index { BOOL, FLOAT, STRING };
static Index type_index(vector_tile::Tile_Value const &v)
{
if(v.has_string_value())
return Index::STRING;
else if(v.has_float_value())
return Index::FLOAT;
else
return Index::BOOL;
}
static bool compare(vector_tile::Tile_Value const &lhs, vector_tile::Tile_Value const &rhs) {
auto lhs_id = type_index(lhs);
auto rhs_id = type_index(lhs);
if(lhs_id < rhs_id)
return true;
if(lhs_id > rhs_id)
return false;
switch(lhs_id) {
case Index::BOOL:
return lhs.bool_value() < rhs.bool_value();
case Index::FLOAT:
return lhs.float_value() < rhs.float_value();
case Index::STRING:
return lhs.string_value() < rhs.string_value();
}
throw std::runtime_error("Invalid type in attribute store");
}
struct key_value_less {
bool operator()(kv_with_minzoom const &lhs, kv_with_minzoom const& rhs) const {
return (lhs.minzoom != rhs.minzoom) ? (lhs.minzoom < rhs.minzoom)
: (lhs.key != rhs.key) ? (lhs.key < rhs.key)
: compare(lhs.value, rhs.value);
}
};
struct key_value_set {
std::set<kv_with_minzoom, key_value_less> values;
mutable std::atomic<uint64_t> references;
key_value_set()
: references(0)
{ }
};
using key_value_set_ref_t = boost::intrusive_ptr<key_value_set>;
struct key_value_set_store_less {
bool operator()(key_value_set_ref_t const &lhs, key_value_set_ref_t const &rhs) const {
if(lhs->values.size() < rhs->values.size()) return true;
if(lhs->values.size() > rhs->values.size()) return false;
key_value_less compare;
for(auto i = lhs->values.begin(), j = rhs->values.begin(); i != lhs->values.end(); ++i, ++j) {
if(compare(*i, *j)) return true;
if(compare(*j, *i)) return false;
}
return false;
}
};
using key_value_set_t = std::set<key_value_set_ref_t, key_value_set_store_less>;
using key_value_index_t = std::pair<Index, char>;
using key_value_map_t = std::vector< std::pair<std::mutex, key_value_set_t> >;
key_value_map_t set_list;
AttributeStore(unsigned int threadNum)
: set_list(threadNum * threadNum)
{ }
key_value_set_ref_t empty_set() const { return new key_value_set(); }
key_value_set_ref_t store_set(key_value_set_ref_t attributes) {
auto idx = attributes->values.size();
for(auto const &i: attributes->values) {
boost::hash_combine(idx, i.minzoom);
boost::hash_combine(idx, i.key);
boost::hash_combine(idx, type_index(i.value));
if(i.value.has_string_value())
boost::hash_combine(idx, i.value.string_value());
else if(i.value.has_float_value())
boost::hash_combine(idx, i.value.float_value());
else
boost::hash_combine(idx, i.value.bool_value());
}
std::lock_guard<std::mutex> lock(set_list[idx % set_list.size()].first);
return *set_list[idx % set_list.size()].second.insert(attributes).first;
}
};
static inline void intrusive_ptr_add_ref(AttributeStore::key_value_set *kv_set){
auto result = kv_set->references.fetch_add(1, std::memory_order_relaxed);
}
static inline void intrusive_ptr_release(AttributeStore::key_value_set *kv_set) {
if (kv_set->references.fetch_sub(1, std::memory_order_release) == 1) {
std::atomic_thread_fence(std::memory_order_acquire);
delete kv_set;
}
}
using AttributeStoreRef = AttributeStore::key_value_set_ref_t;
#endif //_COORDINATES_H