Skip to content

Commit b6258db

Browse files
authored
Merge pull request #2509 from danpoe/feature/sharing-map-stats
Sharing map sharing statistics
2 parents eb71a01 + 4ae8eb6 commit b6258db

File tree

4 files changed

+368
-15
lines changed

4 files changed

+368
-15
lines changed

src/util/sharing_map.h

Lines changed: 201 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ Author: Daniel Poetzl
4444
template <class keyT, class valueT, class hashT, class equalT> \
4545
CV typename sharing_mapt<keyT, valueT, hashT, equalT>::ST \
4646
sharing_mapt<keyT, valueT, hashT, equalT>
47+
48+
#define SHARING_MAPT3(T, CV, ST) \
49+
template <class keyT, class valueT, class hashT, class equalT> \
50+
template <class T> \
51+
CV typename sharing_mapt<keyT, valueT, hashT, equalT>::ST \
52+
sharing_mapt<keyT, valueT, hashT, equalT>
4753
// clang-format on
4854

4955
// Note: Due to a bug in Visual Studio we need to add an additional "const"
@@ -124,11 +130,6 @@ template <class keyT,
124130
class sharing_mapt
125131
{
126132
public:
127-
friend void sharing_map_interface_test();
128-
friend void sharing_map_copy_test();
129-
friend void sharing_map_collision_test();
130-
friend void sharing_map_view_test();
131-
132133
~sharing_mapt()
133134
{
134135
}
@@ -291,6 +292,35 @@ class sharing_mapt
291292
delta_viewt &delta_view,
292293
const bool only_common = true) const;
293294

295+
/// Stats about sharing between several sharing map instances. An instance of
296+
/// this class is returned by the get_sharing_map_stats_* functions.
297+
///
298+
/// The num_nodes field gives the total number of nodes in the given maps.
299+
/// Nodes that are part of n of the maps are counted n times.
300+
///
301+
/// The num_unique_nodes field gives the number of unique nodes in the given
302+
/// maps. A node that is part of several of the maps is only counted once.
303+
///
304+
/// The num_leafs and num_unique_leafs fields are similar to the above but
305+
/// only leafs are counted.
306+
struct sharing_map_statst
307+
{
308+
std::size_t num_nodes = 0;
309+
std::size_t num_unique_nodes = 0;
310+
std::size_t num_leafs = 0;
311+
std::size_t num_unique_leafs = 0;
312+
};
313+
314+
template <class Iterator>
315+
static sharing_map_statst get_sharing_stats(
316+
Iterator begin,
317+
Iterator end,
318+
std::function<sharing_mapt &(const Iterator)> f =
319+
[](const Iterator it) -> sharing_mapt & { return *it; });
320+
321+
template <class Iterator>
322+
static sharing_map_statst get_sharing_stats_map(Iterator begin, Iterator end);
323+
294324
protected:
295325
// helpers
296326

@@ -317,6 +347,11 @@ class sharing_mapt
317347
void gather_all(const baset &n, const unsigned depth, delta_viewt &delta_view)
318348
const;
319349

350+
std::size_t count_unmarked_nodes(
351+
bool leafs_only,
352+
std::set<void *> &marked,
353+
bool mark = true) const;
354+
320355
// dummy element returned when no element was found
321356
static mapped_type dummy;
322357

@@ -386,6 +421,167 @@ ::iterate(
386421
while(!stack.empty());
387422
}
388423

424+
SHARING_MAPT(std::size_t)
425+
::count_unmarked_nodes(bool leafs_only, std::set<void *> &marked, bool mark)
426+
const
427+
{
428+
if(empty())
429+
return 0;
430+
431+
unsigned count = 0;
432+
433+
typedef std::pair<unsigned, const baset *> stack_itemt;
434+
435+
std::stack<stack_itemt> stack;
436+
stack.push({0, &map});
437+
438+
do
439+
{
440+
const stack_itemt &si = stack.top();
441+
442+
const unsigned depth = si.first;
443+
const baset *bp = si.second;
444+
445+
stack.pop();
446+
447+
// internal node or container node
448+
const innert *ip = static_cast<const innert *>(bp);
449+
const unsigned use_count = ip->data.use_count();
450+
void *raw_ptr = ip->data.get();
451+
452+
if(use_count >= 2)
453+
{
454+
if(marked.find(raw_ptr) != marked.end())
455+
{
456+
continue;
457+
}
458+
459+
if(mark)
460+
{
461+
marked.insert(raw_ptr);
462+
}
463+
}
464+
465+
if(!leafs_only)
466+
{
467+
count++;
468+
}
469+
470+
if(depth < steps) // internal
471+
{
472+
const to_mapt &m = ip->get_to_map();
473+
SM_ASSERT(!m.empty());
474+
475+
for(const auto &item : m)
476+
{
477+
const innert *i = &item.second;
478+
stack.push({depth + 1, i});
479+
}
480+
}
481+
else // container
482+
{
483+
SM_ASSERT(depth == steps);
484+
485+
const leaf_listt &ll = ip->get_container();
486+
SM_ASSERT(!ll.empty());
487+
488+
for(const auto &l : ll)
489+
{
490+
const unsigned use_count = l.data.use_count();
491+
void *raw_ptr = l.data.get();
492+
493+
if(use_count >= 2)
494+
{
495+
if(marked.find(raw_ptr) != marked.end())
496+
{
497+
continue;
498+
}
499+
500+
if(mark)
501+
{
502+
marked.insert(raw_ptr);
503+
}
504+
}
505+
506+
count++;
507+
}
508+
}
509+
} while(!stack.empty());
510+
511+
return count;
512+
}
513+
514+
/// Get sharing stats
515+
///
516+
/// Complexity:
517+
/// - Worst case: O(N * H * log(S))
518+
/// - Best case: O(N + H)
519+
///
520+
/// \param begin: begin iterator
521+
/// \param end: end iterator
522+
/// \param f: function applied to the iterator to get a sharing map
523+
/// \return: sharing stats
524+
SHARING_MAPT3(Iterator, , sharing_map_statst)
525+
::get_sharing_stats(
526+
Iterator begin,
527+
Iterator end,
528+
std::function<sharing_mapt &(const Iterator)> f)
529+
{
530+
std::set<void *> marked;
531+
sharing_map_statst sms;
532+
533+
// We do a separate pass over the tree for each statistic. This is not very
534+
// efficient but the function is intended only for diagnosis purposes anyways.
535+
536+
// number of nodes
537+
for(Iterator it = begin; it != end; it++)
538+
{
539+
sms.num_nodes += f(it).count_unmarked_nodes(false, marked, false);
540+
}
541+
542+
SM_ASSERT(marked.empty());
543+
544+
// number of unique nodes
545+
for(Iterator it = begin; it != end; it++)
546+
{
547+
sms.num_unique_nodes += f(it).count_unmarked_nodes(false, marked, true);
548+
}
549+
550+
marked.clear();
551+
552+
// number of leafs
553+
for(Iterator it = begin; it != end; it++)
554+
{
555+
sms.num_leafs += f(it).count_unmarked_nodes(true, marked, false);
556+
}
557+
558+
SM_ASSERT(marked.empty());
559+
560+
// number of unique leafs
561+
for(Iterator it = begin; it != end; it++)
562+
{
563+
sms.num_unique_leafs += f(it).count_unmarked_nodes(true, marked, true);
564+
}
565+
566+
return sms;
567+
}
568+
569+
/// Get sharing stats
570+
///
571+
/// Complexity:
572+
/// - Worst case: O(N * H * log(S))
573+
/// - Best case: O(N + H)
574+
///
575+
/// \param begin: begin iterator of a map
576+
/// \param end: end iterator of a map
577+
/// \return: sharing stats
578+
SHARING_MAPT3(Iterator, , sharing_map_statst)
579+
::get_sharing_stats_map(Iterator begin, Iterator end)
580+
{
581+
return get_sharing_stats<Iterator>(
582+
begin, end, [](const Iterator it) -> sharing_mapt & { return it->second; });
583+
}
584+
389585
/// Get a view of the elements in the map
390586
/// A view is a list of pairs with the components being const references to the
391587
/// keys and values in the map.

src/util/sharing_node.h

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ SN_TYPE_PAR_DEF class sharing_node_innert : public sharing_node_baset
129129

130130
bool shares_with(const sharing_node_innert &other) const
131131
{
132+
SN_ASSERT(data && other.data);
133+
132134
return data == other.data;
133135
}
134136

@@ -151,8 +153,6 @@ SN_TYPE_PAR_DEF class sharing_node_innert : public sharing_node_baset
151153

152154
d_it &write_internal()
153155
{
154-
SN_ASSERT(data.use_count() > 0);
155-
156156
if(data == empty_data)
157157
{
158158
data = make_shared_derived_u<SN_PTR_TYPE_ARGS>();
@@ -176,8 +176,6 @@ SN_TYPE_PAR_DEF class sharing_node_innert : public sharing_node_baset
176176

177177
d_ct &write_container()
178178
{
179-
SN_ASSERT(data.use_count() > 0);
180-
181179
if(data == empty_data)
182180
{
183181
data = make_shared_derived_v<SN_PTR_TYPE_ARGS>();
@@ -259,8 +257,9 @@ SN_TYPE_PAR_DEF class sharing_node_innert : public sharing_node_baset
259257
leaft *place_leaf(const keyT &k, const valueT &v)
260258
{
261259
SN_ASSERT(is_container());
262-
263-
SN_ASSERT(as_const(this)->find_leaf(k) == nullptr);
260+
// we need to check empty() first as the const version of find_leaf() must
261+
// not be called on an empty node
262+
SN_ASSERT(empty() || as_const(this)->find_leaf(k) == nullptr);
264263

265264
leaf_listt &c = get_container();
266265
c.push_front(leaft(k, v));

0 commit comments

Comments
 (0)