From b75737fd6a1dea2fb9531c8e2be91cd502a892fe Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Wed, 3 Mar 2021 15:01:58 +0000 Subject: [PATCH] Implement proj_transform caching using boost::unordered_map which allows calling `find` method with compatible key type. In this case `std::pair` avoiding potentially expensive temp string keys. (TODO: In the future use c++20 `std::unordered_map::find` transparent keys facility) --- .../mapnik/feature_style_processor_impl.hpp | 24 +++------- include/mapnik/map.hpp | 35 ++++++++++++--- src/map.cpp | 44 +++++++++---------- 3 files changed, 54 insertions(+), 49 deletions(-) diff --git a/include/mapnik/feature_style_processor_impl.hpp b/include/mapnik/feature_style_processor_impl.hpp index 3de36ad75..d85c3235d 100644 --- a/include/mapnik/feature_style_processor_impl.hpp +++ b/include/mapnik/feature_style_processor_impl.hpp @@ -66,10 +66,9 @@ struct layer_rendering_material std::vector materials_; layer_rendering_material(layer const& lay, projection const& dest) - : - lay_(lay), - proj0_(dest), - proj1_(lay.srs(),true) {} + : lay_(lay), + proj0_(dest), + proj1_(lay.srs(), true) {} layer_rendering_material(layer_rendering_material && rhs) = default; }; @@ -255,13 +254,7 @@ void feature_style_processor::prepare_layer(layer_rendering_material } processor_context_ptr current_ctx = ds->get_context(ctx_map); - std::string key = mat.proj0_.params() + mat.proj1_.params(); - auto itr = m_.proj_cache().find(key); - if (itr == m_.proj_cache().end()) - { - throw std::runtime_error("Failed to initialise projection transform"); - } - proj_transform * proj_trans_ptr = itr->second.get(); + proj_transform * proj_trans_ptr = m_.get_proj_transform(mat.proj0_.params(), mat.proj1_.params()); box2d query_ext = extent; // unbuffered box2d buffered_query_ext(query_ext); // buffered @@ -500,14 +493,7 @@ void feature_style_processor::render_material(layer_rendering_materia layer const& lay = mat.lay_; std::vector const & rule_caches = mat.rule_caches_; - - std::string key = mat.proj0_.params() + mat.proj1_.params(); - auto itr = m_.proj_cache().find(key); - if (itr == m_.proj_cache().end()) - { - throw std::runtime_error("Failed to initialize projection transform"); - } - proj_transform* proj_trans_ptr = itr->second.get(); + proj_transform * proj_trans_ptr = m_.get_proj_transform(mat.proj0_.params(), mat.proj1_.params()); bool cache_features = lay.cache_features() && active_styles.size() > 1; datasource_ptr ds = lay.datasource(); diff --git a/include/mapnik/map.hpp b/include/mapnik/map.hpp index dabb8653d..73b6f2de7 100644 --- a/include/mapnik/map.hpp +++ b/include/mapnik/map.hpp @@ -38,10 +38,12 @@ MAPNIK_DISABLE_WARNING_PUSH #include #include +#include +#include +#include MAPNIK_DISABLE_WARNING_POP // stl -#include #include #include #include @@ -57,8 +59,32 @@ class layer; class MAPNIK_DECL Map : boost::equality_comparable { - using proj_cache_type = std::unordered_map>; public: + using key_type = std::pair; + using compatible_key_type = std::pair; + + struct compatible_hash + { + template + std::size_t operator() (KeyType const& key) const + { + using hash_type = boost::hash; + std::size_t seed = hash_type{}(key.first); + seed ^= hash_type{}(key.second) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + return seed; + } + }; + + struct compatible_predicate + { + bool operator()(compatible_key_type const& k1, + compatible_key_type const& k2) const + { + return k1 == k2; + } + }; + + using proj_cache_type = boost::unordered_map, compatible_hash>; enum aspect_fix_mode { @@ -492,11 +518,8 @@ public: { return font_memory_cache_; } - proj_cache_type const& proj_cache() const - { - return proj_cache_; - } + proj_transform * get_proj_transform(std::string const& source, std::string const& dest) const; private: friend void swap(Map & rhs, Map & lhs); void fixAspectRatio(); diff --git a/src/map.cpp b/src/map.cpp index 8e943bbf1..374b8212a 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -323,15 +323,27 @@ size_t Map::layer_count() const return layers_.size(); } +proj_transform * Map::get_proj_transform(std::string const& source, std::string const& dest) const +{ + compatible_key_type key = std::make_pair(source, dest); + auto itr = proj_cache_.find(key, compatible_hash{}, compatible_predicate{}); + if (itr == proj_cache_.end()) + { + throw std::runtime_error("Failed to initialise projection transform:" + + key.first.to_string() + " -> " + key.second.to_string()); + } + return itr->second.get(); +} + void Map::add_layer(layer const& l) { - std::string key = srs_ + l.srs(); - auto itr = proj_cache_.find(key); + compatible_key_type key = std::make_pair(srs_, l.srs()); + auto itr = proj_cache_.find(key, compatible_hash{}, compatible_predicate{}); if (itr == proj_cache_.end()) { mapnik::projection source(srs_, true); mapnik::projection dest(l.srs(), true); - proj_cache_.emplace(key, + proj_cache_.emplace(std::make_pair(srs_, l.srs()), std::make_unique(source, dest)); } layers_.emplace_back(l); @@ -339,13 +351,13 @@ void Map::add_layer(layer const& l) void Map::add_layer(layer && l) { - std::string key = srs_ + l.srs(); - auto itr = proj_cache_.find(key); + compatible_key_type key = std::make_pair(srs_, l.srs()); + auto itr = proj_cache_.find(key, compatible_hash{}, compatible_predicate{}); if (itr == proj_cache_.end()) { mapnik::projection source(srs_, true); mapnik::projection dest(l.srs(), true); - proj_cache_.emplace(key, + proj_cache_.emplace(make_pair(srs_, l.srs()), std::make_unique(source, dest)); } layers_.push_back(std::move(l)); @@ -535,15 +547,7 @@ void Map::zoom_all() if (layer.active()) { std::string const& layer_srs = layer.srs(); - - std::string key = srs_ + layer_srs; - auto itr = proj_cache_.find(key); - - if (itr == proj_cache_.end()) - { - throw std::runtime_error("Failed to initialise projection transform"); - } - proj_transform* proj_trans_ptr = itr->second.get(); + proj_transform * proj_trans_ptr = get_proj_transform(srs_, layer_srs);; box2d layer_ext = layer.envelope(); if (proj_trans_ptr->backward(layer_ext, PROJ_ENVELOPE_POINTS)) { @@ -723,15 +727,7 @@ featureset_ptr Map::query_point(unsigned index, double x, double y) const mapnik::datasource_ptr ds = layer.datasource(); if (ds) { - std::string key = srs_ + layer.srs(); - auto itr = proj_cache_.find(key); - - if (itr == proj_cache_.end()) - { - throw std::runtime_error("Failed to initialise projection transform"); - } - proj_transform * proj_trans_ptr = itr->second.get(); - + proj_transform * proj_trans_ptr = get_proj_transform(srs_ ,layer.srs()); double z = 0; if (!proj_trans_ptr->equal() && !proj_trans_ptr->backward(x,y,z)) {