massively improved segment lookup time

This commit is contained in:
joaquintides 2017-06-25 22:34:44 +02:00
parent 3bdf47cb05
commit c5f6e83938
2 changed files with 187 additions and 31 deletions

View File

@ -22,12 +22,11 @@
#include <boost/poly_collection/detail/is_final.hpp>
#include <boost/poly_collection/detail/newdelete_allocator.hpp>
#include <boost/poly_collection/detail/segment.hpp>
#include <boost/poly_collection/detail/type_info_map.hpp>
#include <boost/poly_collection/exception.hpp>
#include <iterator>
#include <new>
#include <type_traits>
#include <typeinfo>
#include <unordered_map>
#include <utility>
namespace boost{
@ -40,17 +39,6 @@ namespace poly_collection_impl{
/* common implementation for all polymorphic collections */
struct type_info_ptr_hash
{
std::size_t operator()(const std::type_info* p)const{return p->hash_code();}
};
struct type_info_ptr_equal_to
{
bool operator()(const std::type_info* p,const std::type_info* q)const
{return *p==*q;}
};
template<typename Model,typename Allocator>
class poly_collection
{
@ -127,13 +115,11 @@ class poly_collection
template<typename T>
using const_segment_iterator=
typename segment_type::template const_iterator<T>;
using segment_map=std::unordered_map<
const std::type_info*,segment_type,
type_info_ptr_hash,type_info_ptr_equal_to,
using segment_map=type_info_map<
segment_type,
newdelete_allocator_adaptor<
typename std::allocator_traits<Allocator>::template
rebind_alloc<std::pair<const std::type_info* const,segment_type>
>
rebind_alloc<segment_type>
>
>;
using segment_map_allocator_type=typename segment_map::allocator_type;
@ -432,13 +418,13 @@ public:
(void)seq{
0,
(map.insert(
{&typeid(T),segment_type::template make<T>(get_allocator())}),0)...
typeid(T),segment_type::template make<T>(get_allocator())),0)...
};
}
bool is_registered(const std::type_info& info)const
{
return map.find(&info)!=map.end();
return map.find(info)!=map.end();
}
template<typename T,enable_if_acceptable<T> =nullptr>
@ -981,11 +967,11 @@ private:
const_segment_map_iterator get_map_iterator_for(const T& x)
{
const auto& id=subtypeid(x);
auto it=map.find(&id);
auto it=map.find(id);
if(it!=map.end())return it;
else if(id!=typeid(T))throw unregistered_type{id};
else return map.insert(
{&typeid(T),segment_type::template make<T>(get_allocator())}).first;
typeid(T),segment_type::template make<T>(get_allocator())).first;
}
template<
@ -995,10 +981,10 @@ private:
>
const_segment_map_iterator get_map_iterator_for(const T&)
{
auto it=map.find(&typeid(T));
auto it=map.find(typeid(T));
if(it!=map.end())return it;
else return map.insert(
{&typeid(T),segment_type::template make<T>(get_allocator())}).first;
typeid(T),segment_type::template make<T>(get_allocator())).first;
}
template<
@ -1009,7 +995,7 @@ private:
const_segment_map_iterator get_map_iterator_for(const T& x)const
{
const auto& id=subtypeid(x);
auto it=map.find(&id);
auto it=map.find(id);
if(it!=map.end())return it;
else throw unregistered_type{id};
}
@ -1032,18 +1018,18 @@ private:
const T& x,const segment_type& seg)
{
const auto& id=subtypeid(x);
auto it=map.find(&id);
auto it=map.find(id);
if(it!=map.end())return it;
else return map.insert({&id,segment_type::make_from_prototype(seg)}).first;
else return map.insert(id,segment_type::make_from_prototype(seg)).first;
}
template<typename T>
const_segment_map_iterator get_map_iterator_for()
{
auto it=map.find(&typeid(T));
auto it=map.find(typeid(T));
if(it!=map.end())return it;
else return map.insert(
{&typeid(T),segment_type::template make<T>(get_allocator())}).first;
typeid(T),segment_type::template make<T>(get_allocator())).first;
}
const_segment_map_iterator get_map_iterator_for(const std::type_info& info)
@ -1055,7 +1041,7 @@ private:
const_segment_map_iterator get_map_iterator_for(
const std::type_info& info)const
{
auto it=map.find(&info);
auto it=map.find(info);
if(it!=map.end())return it;
else throw unregistered_type{info};
}
@ -1157,7 +1143,7 @@ bool operator==(
const auto &mapx=x.map,&mapy=y.map;
for(const auto& p:mapx){
auto ss=p.second.size();
auto it=mapy.find(p.first);
auto it=mapy.find(*p.first);
if(it==mapy.end()?ss!=0:p.second!=it->second)return false;
s+=ss;
}

View File

@ -0,0 +1,170 @@
/* Copyright 2016-2017 Joaquin M Lopez Munoz.
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* See http://www.boost.org/libs/poly_collection for library home page.
*/
#ifndef BOOST_POLY_COLLECTION_DETAIL_TYPE_INFO_MAP_HPP
#define BOOST_POLY_COLLECTION_DETAIL_TYPE_INFO_MAP_HPP
#if defined(_MSC_VER)
#pragma once
#endif
#include <boost/poly_collection/detail/newdelete_allocator.hpp>
#include <functional>
#include <typeinfo>
#include <unordered_map>
#include <utility>
namespace boost{
namespace poly_collection{
namespace detail{
/* To cope with dynamic modules/libs, the standard allows for different
* std::type_info instances to describe the same type, which implies that
* std::type_info::operator== and std::type_info::hash_code are costly
* operations typically relying on the stored type name.
* type_info_ptr_hash<T> behaves roughly as a
* std::unordered_map<std::type_index,T> but maintains an internal cache of
* passed std::type_info instances so that lookup is performed (when there's a
* cache hit) without invoking std::type_info equality and hashing ops.
*/
struct type_info_ptr_hash
{
std::size_t operator()(const std::type_info* p)const noexcept
{return p->hash_code();}
};
struct type_info_ptr_equal_to
{
bool operator()(
const std::type_info* p,const std::type_info* q)const noexcept
{return *p==*q;}
};
template<typename T,typename Allocator>
class type_info_map
{
using map_type=std::unordered_map<
const std::type_info*,T,
type_info_ptr_hash,type_info_ptr_equal_to,
typename std::allocator_traits<Allocator>::template
rebind_alloc<std::pair<const std::type_info* const,T>>
>;
public:
using key_type=std::type_info;
using mapped_type=T;
using value_type=typename map_type::value_type;
using allocator_type=typename map_type::allocator_type;
using iterator=typename map_type::iterator;
using const_iterator=typename map_type::const_iterator;
type_info_map()=default;
type_info_map(const type_info_map& x):
map{x.map},cache{x.cache.get_allocator()}{build_cache(x.cache);}
type_info_map(type_info_map&& x)=default;
type_info_map(const allocator_type& al):
map{al},cache{cache_allocator_type{al}}{}
type_info_map(const type_info_map& x,const allocator_type& al):
map{x.map,al},cache{cache_allocator_type{al}}{build_cache(x.cache);}
type_info_map(type_info_map&& x,const allocator_type& al):
map{std::move(x.map),al},cache{cache_allocator_type{al}}
{
if(al==x.map.get_allocator()&&
cache_allocator_type{al}==x.cache.get_allocator()){
cache=std::move(x.cache);
}
else{
build_cache(x.cache);
x.cache.clear();
}
}
type_info_map& operator=(const type_info_map& x)
{
if(this!=&x){
map=x.map;
cache=cache_type{x.cache.get_allocator()};
build_cache(x.cache);
}
return *this;
}
type_info_map& operator=(type_info_map&& x)=default;
allocator_type get_allocator()const noexcept{return map.get_allocator();}
iterator begin()noexcept{return map.begin();}
iterator end()noexcept{return map.end();}
const_iterator begin()const noexcept{return map.begin();}
const_iterator end()const noexcept{return map.end();}
const_iterator cbegin()const noexcept{return map.cbegin();}
const_iterator cend()const noexcept{return map.cend();}
iterator find(const key_type& key)
{
auto cit=cache.find(&key);
if(cit!=cache.end())return cit->second;
auto mit=map.find(&key);
if(mit!=map.end())cache.insert({&key,mit});
return mit;
}
const_iterator find(const key_type& key)const
{
auto cit=cache.find(&key);
if(cit!=cache.end())return cit->second;
return map.find(&key);
}
template<typename P>
std::pair<iterator,bool> insert(const key_type& key,P&& x)
{
auto p=map.insert({&key,std::forward<P>(x)});
cache.insert({&key,p.first});
return p;
}
void swap(type_info_map& x){map.swap(x.map);cache.swap(x.cache);}
private:
using cache_type=std::unordered_map<
const std::type_info*,iterator,
std::hash<const std::type_info*>,std::equal_to<const std::type_info*>,
newdelete_allocator_adaptor<
typename std::allocator_traits<Allocator>::template
rebind_alloc<std::pair<const std::type_info* const,iterator>>
>
>;
using cache_allocator_type=typename cache_type::allocator_type;
void build_cache(const cache_type& x)
{
for(const auto& p:x)cache.insert({p.first,map.find(p.first)});
}
map_type map;
cache_type cache;
};
template<typename T,typename Allocator>
void swap(type_info_map<T,Allocator>& x,type_info_map<T,Allocator>& y)
{
x.swap(y);
}
} /* namespace poly_collection::detail */
} /* namespace poly_collection */
} /* namespace boost */
#endif