added stats to foa::table

This commit is contained in:
joaquintides
2024-04-28 18:44:23 +02:00
parent 73582be563
commit e712c37e9f
3 changed files with 247 additions and 0 deletions

View 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

View File

@ -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);

View File

@ -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)
{