mirror of
https://github.com/boostorg/unordered.git
synced 2025-07-29 19:07:15 +02:00
added stats to foa::table
This commit is contained in:
155
include/boost/unordered/detail/cumulative_stats.hpp
Normal file
155
include/boost/unordered/detail/cumulative_stats.hpp
Normal file
@ -0,0 +1,155 @@
|
||||
/* Copyright 2024 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 https://www.boost.org/libs/unordered for library home page.
|
||||
*/
|
||||
|
||||
#ifndef BOOST_UNORDERED_DETAIL_CUMULATIVE_STATS_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_CUMULATIVE_STATS_HPP
|
||||
|
||||
#include <array>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/mp11/tuple.hpp>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
|
||||
#if defined(BOOST_HAS_THREADS)
|
||||
#include <mutex>
|
||||
#endif
|
||||
|
||||
namespace boost{
|
||||
namespace unordered{
|
||||
namespace detail{
|
||||
|
||||
/* Cumulative one-pass calculation of the average, variance and deviation of
|
||||
* running sequences.
|
||||
*/
|
||||
|
||||
struct cumulative_stats_summary
|
||||
{
|
||||
double average;
|
||||
double variance;
|
||||
double deviation;
|
||||
};
|
||||
|
||||
struct cumulative_stats_data
|
||||
{
|
||||
double m=0.0;
|
||||
double m_prior=0.0;
|
||||
double s=0.0;
|
||||
};
|
||||
|
||||
struct welfords_algorithm /* 0-based */
|
||||
{
|
||||
template<typename T>
|
||||
int operator()(T&& x,cumulative_stats_data& d)const
|
||||
{
|
||||
d.m_prior=d.m;
|
||||
d.m+=(static_cast<double>(x)-d.m)/static_cast<double>(n);
|
||||
d.s+=(n!=1)*
|
||||
(static_cast<double>(x)-d.m_prior)*(static_cast<double>(x)-d.m);
|
||||
|
||||
return 0; /* mp11::tuple_transform requires that return type not be void */
|
||||
}
|
||||
|
||||
std::size_t n;
|
||||
};
|
||||
|
||||
/* Stats calculated jointly for N same-sized sequences to save the space
|
||||
* for n.
|
||||
*/
|
||||
|
||||
template<std::size_t N>
|
||||
class cumulative_stats
|
||||
{
|
||||
public:
|
||||
void reset()noexcept{*this=cumulative_stats();}
|
||||
|
||||
template<typename... Ts>
|
||||
void add(Ts&&... xs)
|
||||
{
|
||||
static_assert(
|
||||
sizeof...(Ts)==N,"A sample must be provided for each sequence.");
|
||||
|
||||
mp11::tuple_transform(
|
||||
welfords_algorithm{++n},
|
||||
std::forward_as_tuple(std::forward<Ts>(xs)...),
|
||||
data);
|
||||
}
|
||||
|
||||
template<std::size_t I>
|
||||
cumulative_stats_summary get_summary()const noexcept
|
||||
{
|
||||
double average=data[I].m,
|
||||
variance=n!=0?data[I].s/static_cast<double>(n):0.0, /* biased */
|
||||
deviation=std::sqrt(variance);
|
||||
return {average,variance,deviation};
|
||||
}
|
||||
|
||||
private:
|
||||
std::size_t n=0;
|
||||
std::array<cumulative_stats_data,N> data;
|
||||
};
|
||||
|
||||
#if defined(BOOST_HAS_THREADS)
|
||||
|
||||
template<std::size_t N>
|
||||
class concurrent_cumulative_stats:cumulative_stats<N>
|
||||
{
|
||||
using super=cumulative_stats<N>;
|
||||
using lock_guard=std::lock_guard<std::mutex>;
|
||||
|
||||
public:
|
||||
concurrent_cumulative_stats()noexcept:super{}{}
|
||||
concurrent_cumulative_stats(const concurrent_cumulative_stats& x)noexcept:
|
||||
concurrent_cumulative_stats{x,lock_guard{x.mut}}{}
|
||||
|
||||
concurrent_cumulative_stats&
|
||||
operator=(const concurrent_cumulative_stats& x)noexcept
|
||||
{
|
||||
auto x1=x;
|
||||
lock_guard lck{mut};
|
||||
static_cast<super&>(*this)=x1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void reset()noexcept
|
||||
{
|
||||
lock_guard lck{mut};
|
||||
super::reset();
|
||||
}
|
||||
|
||||
template<typename... Ts>
|
||||
void add(Ts&&... xs)
|
||||
{
|
||||
lock_guard lck{mut};
|
||||
super::add(std::forward<Ts>(xs)...);
|
||||
}
|
||||
|
||||
template<std::size_t I>
|
||||
cumulative_stats_summary get_summary()const noexcept
|
||||
{
|
||||
lock_guard lck{mut};
|
||||
return super::template get_summary<I>();
|
||||
}
|
||||
|
||||
private:
|
||||
concurrent_cumulative_stats(const super& x,lock_guard&&):super{x}{}
|
||||
|
||||
mutable std::mutex mut;
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
template<std::size_t N>
|
||||
using concurrent_cumulative_stats=cumulative_stats<N>;
|
||||
|
||||
#endif
|
||||
|
||||
} /* namespace detail */
|
||||
} /* namespace unordered */
|
||||
} /* namespace boost */
|
||||
|
||||
#endif
|
@ -40,6 +40,10 @@
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#if defined(BOOST_UNORDERED_ENABLE_STATS)
|
||||
#include <boost/unordered/detail/cumulative_stats.hpp>
|
||||
#endif
|
||||
|
||||
#if !defined(BOOST_UNORDERED_DISABLE_SSE2)
|
||||
#if defined(BOOST_UNORDERED_ENABLE_SSE2)|| \
|
||||
defined(__SSE2__)|| \
|
||||
@ -864,6 +868,7 @@ struct pow2_quadratic_prober
|
||||
pow2_quadratic_prober(std::size_t pos_):pos{pos_}{}
|
||||
|
||||
inline std::size_t get()const{return pos;}
|
||||
inline std::size_t length()const{return step+1;}
|
||||
|
||||
/* next returns false when the whole array has been traversed, which ends
|
||||
* probing (in practice, full-table probing will only happen with very small
|
||||
@ -1125,6 +1130,39 @@ struct table_arrays
|
||||
value_type_pointer elements_;
|
||||
};
|
||||
|
||||
#if defined(BOOST_UNORDERED_ENABLE_STATS)
|
||||
/* stats support */
|
||||
|
||||
struct table_core_insertion_stats
|
||||
{
|
||||
cumulative_stats_summary probe_length;
|
||||
};
|
||||
|
||||
struct table_core_lookup_stats
|
||||
{
|
||||
cumulative_stats_summary probe_length;
|
||||
cumulative_stats_summary num_comparisons;
|
||||
};
|
||||
|
||||
struct table_core_stats
|
||||
{
|
||||
table_core_insertion_stats insertion;
|
||||
table_core_lookup_stats successful_lookup,
|
||||
unsuccessful_lookup;
|
||||
};
|
||||
|
||||
#define BOOST_UNORDERED_ADD_STATS(stats,args) stats.add args
|
||||
#define BOOST_UNORDERED_STATS_COUNTER(name) std::size_t name=0
|
||||
#define BOOST_UNORDERED_INCREMENT_STATS_COUNTER(name) ++name
|
||||
|
||||
#else
|
||||
|
||||
#define BOOST_UNORDERED_ADD_STATS(stats,args)
|
||||
#define BOOST_UNORDERED_STATS_COUNTER(name)
|
||||
#define BOOST_UNORDERED_INCREMENT_STATS_COUNTER(name)
|
||||
|
||||
#endif
|
||||
|
||||
struct if_constexpr_void_else{void operator()()const{}};
|
||||
|
||||
template<bool B,typename F,typename G=if_constexpr_void_else>
|
||||
@ -1395,6 +1433,10 @@ public:
|
||||
using locator=table_locator<group_type,element_type>;
|
||||
using arrays_holder_type=arrays_holder<arrays_type,Allocator>;
|
||||
|
||||
#if defined(BOOST_UNORDERED_ENABLE_STATS)
|
||||
using stats=table_core_stats;
|
||||
#endif
|
||||
|
||||
table_core(
|
||||
std::size_t n=default_bucket_count,const Hash& h_=Hash(),
|
||||
const Pred& pred_=Pred(),const Allocator& al_=Allocator()):
|
||||
@ -1639,6 +1681,7 @@ public:
|
||||
BOOST_FORCEINLINE locator find(
|
||||
const Key& x,std::size_t pos0,std::size_t hash)const
|
||||
{
|
||||
BOOST_UNORDERED_STATS_COUNTER(num_cmps);
|
||||
prober pb(pos0);
|
||||
do{
|
||||
auto pos=pb.get();
|
||||
@ -1650,18 +1693,25 @@ public:
|
||||
auto p=elements+pos*N;
|
||||
BOOST_UNORDERED_PREFETCH_ELEMENTS(p,N);
|
||||
do{
|
||||
BOOST_UNORDERED_INCREMENT_STATS_COUNTER(num_cmps);
|
||||
auto n=unchecked_countr_zero(mask);
|
||||
if(BOOST_LIKELY(bool(pred()(x,key_from(p[n]))))){
|
||||
BOOST_UNORDERED_ADD_STATS(
|
||||
successful_lookup_stats,(pb.length(),num_cmps));
|
||||
return {pg,n,p+n};
|
||||
}
|
||||
mask&=mask-1;
|
||||
}while(mask);
|
||||
}
|
||||
if(BOOST_LIKELY(pg->is_not_overflowed(hash))){
|
||||
BOOST_UNORDERED_ADD_STATS(
|
||||
unsuccessful_lookup_stats,(pb.length(),num_cmps));
|
||||
return {};
|
||||
}
|
||||
}
|
||||
while(BOOST_LIKELY(pb.next(arrays.groups_size_mask)));
|
||||
BOOST_UNORDERED_ADD_STATS(
|
||||
unsuccessful_lookup_stats,(pb.length(),num_cmps));
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -1746,6 +1796,32 @@ public:
|
||||
rehash(std::size_t(std::ceil(float(n)/mlf)));
|
||||
}
|
||||
|
||||
#if defined(BOOST_UNORDERED_ENABLE_STATS)
|
||||
stats get_stats()const
|
||||
{
|
||||
return {
|
||||
{
|
||||
insertion_stats.get_summary<0>()
|
||||
},
|
||||
{
|
||||
successful_lookup_stats.get_summary<0>(),
|
||||
successful_lookup_stats.get_summary<1>()
|
||||
},
|
||||
{
|
||||
unsuccessful_lookup_stats.get_summary<0>(),
|
||||
unsuccessful_lookup_stats.get_summary<1>()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void reset_stats()
|
||||
{
|
||||
insertion_stats.reset();
|
||||
successful_lookup_stats.reset();
|
||||
unsuccessful_lookup_stats.reset();
|
||||
}
|
||||
#endif
|
||||
|
||||
friend bool operator==(const table_core& x,const table_core& y)
|
||||
{
|
||||
return
|
||||
@ -1956,6 +2032,12 @@ public:
|
||||
arrays_type arrays;
|
||||
size_ctrl_type size_ctrl;
|
||||
|
||||
#if defined(BOOST_UNORDERED_ENABLE_STATS)
|
||||
concurrent_cumulative_stats<1> insertion_stats;
|
||||
mutable concurrent_cumulative_stats<2> successful_lookup_stats,
|
||||
unsuccessful_lookup_stats;
|
||||
#endif
|
||||
|
||||
private:
|
||||
template<
|
||||
typename,typename,template<typename...> class,
|
||||
@ -2243,6 +2325,7 @@ private:
|
||||
auto p=arrays_.elements()+pos*N+n;
|
||||
construct_element(p,std::forward<Args>(args)...);
|
||||
pg->set(n,hash);
|
||||
BOOST_UNORDERED_ADD_STATS(insertion_stats,(pb.length()));
|
||||
return {pg,n,p};
|
||||
}
|
||||
else pg->mark_overflow(hash);
|
||||
|
@ -361,6 +361,10 @@ public:
|
||||
const_iterator>::type;
|
||||
using erase_return_type=table_erase_return_type<iterator>;
|
||||
|
||||
#if defined(BOOST_UNORDERED_ENABLE_STATS)
|
||||
using stats=typename super::stats;
|
||||
#endif
|
||||
|
||||
table(
|
||||
std::size_t n=default_bucket_count,const Hash& h_=Hash(),
|
||||
const Pred& pred_=Pred(),const Allocator& al_=Allocator()):
|
||||
@ -542,6 +546,11 @@ public:
|
||||
using super::rehash;
|
||||
using super::reserve;
|
||||
|
||||
#if defined(BOOST_UNORDERED_ENABLE_STATS)
|
||||
using super::get_stats;
|
||||
using super::reset_stats;
|
||||
#endif
|
||||
|
||||
template<typename Predicate>
|
||||
friend std::size_t erase_if(table& x,Predicate& pr)
|
||||
{
|
||||
|
Reference in New Issue
Block a user