[Multi-Index] Skip-Scan "virtual" ordered-non-unique index on top of composite ordered-unique index
We have BMIs of entries with complex composite "natural-keys". For example:
typedef boost::multi_index_container<
FooIdPtr, // FYI: boost::shared_ptr<FooId>
bmi::indexed_by<
EntityIndex<ByUid>::unique, // FYI: Surrogate/Primary Key
EntityIndex<ByNKey>::of<FooId>::unique, // FYI: Natural Key
// additional indexes by type, name and parent
EntityIndex<ByType>::non_unique,
EntityIndex<ByName>::non_unique,
EntityIndex<ByParent>::non_unique
>
> Impl;
The EntityIndex template utility simply factors the index definitions used
in several BMIs. For example
template <> struct EntityIndex<ByNKey> {
template <typename T_IDENT> struct of {
typedef hashed_unique< // natural key (logical primary key)
tag<ByNKey>, identity< shared_ptr
Dominique Devienne
We have BMIs of entries with complex composite "natural-keys". [...] After recently reading http://www.sqlite.org/draft/optoverview.html#skipscan and its Skip-Scan optimization, I wondered about changing my ByNKey unique index from hashed to ordered, and thought that in theory, I could perhaps avoid the maintenance and memory cost of the additional type+name+parent indexes, since all three are subsets of the ByNKey unique index, and using a similar skip-scan strategy, which might/would still provide reasonable performance, at least better than no index at all (i.e. a full scan).
This is an interesting idea! Without making any internal change to
Boost.MultiIndex indices, a decent approximation to skip-scanning can
be achieved. The code pasted at the bottom provides a function skip_scan
that you can use like this
// scan elements with keys *,1,2,3
skip_scan<1>(
c,[](const element& v){std::cout<
{};
template<> struct make_index_sequence_help<0>:index_sequence<>{};
template<> struct make_index_sequence_help<1>:index_sequence<0>{};
template<int n> using make_index_sequence=invoke
void skip_scan(const Container& c,F f,Args&&... args)
{
skip_scan_helper<N>(c,f,Indices(),std::forward<Args>(args)...);
}
// testing
#include
multi_t;
int main()
{
multi_t c={
{0,1,2,3,4},
{0,1,2,3,5},
{0,1,3,4,0},
{1,0,0,0,0},
{1,1,2,3,0},
{1,1,2,3,0},
{2,1,2,1,0},
{2,2,2,3,0},
};
skip_scan<1>(
c,[](const element& v){std::cout<
On Thu, Nov 28, 2013 at 8:37 PM, Joaquin M Lopez Munoz
Code requires C++11 support :-/
Variadic templates, including sizeof...() decltype trailing return uniform initialization lambdas template alias perfect forwarding with rvalue-refs auto type deduction You've gone all in :). You "almost" could use range-for loop! Needless to say, this is going to require more than my current VS2010. Thanks for your interest on this, and the concrete example. I'll try to digest it, at my own pace though :). --DD
Dominique Devienne
On Thu, Nov 28, 2013 at 8:37 PM, Joaquin M Lopez Munoz
wrote: Code requires C++11 support :-/ Needless to say, this is going to require more than my current VS2010.
This can be backported to C++03 using the awesome Boost.Preprocessor
library. Code above has been tried with VS2012 but should work in your
compiler.
Joaquín M López Muñoz
Telefónica Digital
#include
\ static void invoke( \ const Container& c,F f \ BOOST_PP_COMMA_IF(m) \ BOOST_PP_ENUM_BINARY_PARAMS(m,const T, &t)) \ { \ for(auto it=c.begin(),it_end=c.end();it!=it_end;){ \ for(auto rng=c.equal_range( \ boost::make_tuple( \ BOOST_PP_ENUM(n,SKIP_SCAN_TUPLE_FIRST,~) \ BOOST_PP_COMMA_IF(BOOST_PP_AND(n,m)) \ BOOST_PP_ENUM_PARAMS(m,t))); \ rng.first!=rng.second;++rng.first){ \ f(*rng.first); \ } \ it=c.upper_bound( \ boost::make_tuple(BOOST_PP_ENUM(n,SKIP_SCAN_TUPLE_FIRST,~))); \ } \ }
#define SKIP_SCAN_CLASS_SPECIALIZE(z,n,_) \ template<> struct skip_scan_class<n> \ { \ BOOST_PP_REPEAT( \ BOOST_PP_ADD(BOOST_PP_SUB(SKIP_SCAN_MAX_KEYS,n),1), \ SKIP_SCAN_CLASS_INVOKE,n) \ }; #define SKIP_SCAN_OVERLOAD(z,m,_) \ template< \ int N,typename Container,typename F \ BOOST_PP_COMMA_IF(m) \ BOOST_PP_ENUM_PARAMS(m,typename T) \
\ void skip_scan( \ const Container& c,F f \ BOOST_PP_COMMA_IF(m) \ BOOST_PP_ENUM_BINARY_PARAMS(m,const T, &t)) \ { \ skip_scan_class<N>::invoke( \ c,f \ BOOST_PP_COMMA_IF(m) \ BOOST_PP_ENUM_PARAMS(m,t) \ ); \ }
template
multi_t;
static inline void print_element(const element& v){std::cout<
Joaquin M Lopez Munoz
Dominique Devienne
writes: On Thu, Nov 28, 2013 at 8:37 PM, Joaquin M Lopez Munoz
wrote: Code requires C++11 support :-/ Needless to say, this is going to require more than my current VS2010.
This can be backported to C++03 using the awesome Boost.Preprocessor library. Code above has been tried with VS2012 but should work in your compiler.
Previous code still used auto, tailing return and decltype, fixed now.
Joaquín M López Muñoz
Telefónica Digital
#include
\ static void invoke( \ const Container& c,F f \ BOOST_PP_COMMA_IF(m) \ BOOST_PP_ENUM_BINARY_PARAMS(m,const T, &t)) \ { \ typedef typename Container::iterator iterator; \ for(iterator it=c.begin(),it_end=c.end();it!=it_end;){ \ for(std::pair
rng=c.equal_range( \ boost::make_tuple( \ BOOST_PP_ENUM(n,SKIP_SCAN_TUPLE_FIRST,~) \ BOOST_PP_COMMA_IF(BOOST_PP_AND(n,m)) \ BOOST_PP_ENUM_PARAMS(m,t))); \ rng.first!=rng.second;++rng.first){ \ f(*rng.first); \ } \ it=c.upper_bound( \ boost::make_tuple(BOOST_PP_ENUM(n,SKIP_SCAN_TUPLE_FIRST,~))); \ } \ }
#define SKIP_SCAN_CLASS_SPECIALIZE(z,n,_) \ template<> struct skip_scan_class<n> \ { \ BOOST_PP_REPEAT( \ BOOST_PP_ADD(BOOST_PP_SUB(SKIP_SCAN_MAX_KEYS,n),1), \ SKIP_SCAN_CLASS_INVOKE,n) \ }; #define SKIP_SCAN_OVERLOAD(z,m,_) \ template< \ int N,typename Container,typename F \ BOOST_PP_COMMA_IF(m) \ BOOST_PP_ENUM_PARAMS(m,typename T) \
\ void skip_scan( \ const Container& c,F f \ BOOST_PP_COMMA_IF(m) \ BOOST_PP_ENUM_BINARY_PARAMS(m,const T, &t)) \ { \ skip_scan_class<N>::invoke( \ c,f \ BOOST_PP_COMMA_IF(m) \ BOOST_PP_ENUM_PARAMS(m,t) \ ); \ }
template
::type::result_type key(const CompositeKey& ck,const Value& v) { return boost::tuples::get<N>(ck.key_extractors())(v); }
template<int N>
struct skip_scan_class;
BOOST_PP_REPEAT(
BOOST_PP_ADD(SKIP_SCAN_MAX_KEYS,1),SKIP_SCAN_CLASS_SPECIALIZE,~)
BOOST_PP_REPEAT(
BOOST_PP_ADD(SKIP_SCAN_MAX_KEYS,1),SKIP_SCAN_OVERLOAD,~)
#undef SKIP_SCAN_OVERLOAD
#undef SKIP_SCAN_CLASS_SPECIALIZE
#undef SKIP_SCAN_CLASS_INVOKE
#undef SKIP_SCAN_TUPLE_FIRST
// testing
#include
multi_t;
static inline void print_element(const element& v){std::cout<
participants (2)
-
Dominique Devienne
-
Joaquin M Lopez Munoz