@@ -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"
@@ -128,6 +134,7 @@ class sharing_mapt
128134 friend void sharing_map_copy_test ();
129135 friend void sharing_map_collision_test ();
130136 friend void sharing_map_view_test ();
137+ friend void sharing_map_sharing_stats_test ();
131138
132139 ~sharing_mapt ()
133140 {
@@ -291,6 +298,35 @@ class sharing_mapt
291298 delta_viewt &delta_view,
292299 const bool only_common = true ) const ;
293300
301+ // / Stats about sharing between several sharing map instances. An instance of
302+ // / this class is returned by the get_sharing_map_stats_* functions.
303+ // /
304+ // / The num_nodes field gives the total number of nodes in the given maps.
305+ // / Nodes that are part of n of the maps are counted n times.
306+ // /
307+ // / The num_unique_nodes field gives the number of unique nodes in the given
308+ // / maps. A node that is part of several of the maps is only counted once.
309+ // /
310+ // / The num_leafs and num_unique_leafs fields are similar to the above but
311+ // / only leafs are counted.
312+ struct sharing_map_statst
313+ {
314+ std::size_t num_nodes = 0 ;
315+ std::size_t num_unique_nodes = 0 ;
316+ std::size_t num_leafs = 0 ;
317+ std::size_t num_unique_leafs = 0 ;
318+ };
319+
320+ template <class Iterator >
321+ static sharing_map_statst get_sharing_stats (
322+ Iterator begin,
323+ Iterator end,
324+ std::function<sharing_mapt &(const Iterator)> f =
325+ [](const Iterator it) -> sharing_mapt & { return *it; });
326+
327+ template <class Iterator >
328+ static sharing_map_statst get_sharing_stats_map (Iterator begin, Iterator end);
329+
294330protected:
295331 // helpers
296332
@@ -317,6 +353,11 @@ class sharing_mapt
317353 void gather_all (const baset &n, const unsigned depth, delta_viewt &delta_view)
318354 const ;
319355
356+ std::size_t count_unmarked_nodes (
357+ bool leafs_only,
358+ std::set<void *> &marked,
359+ bool mark = true ) const ;
360+
320361 // dummy element returned when no element was found
321362 static mapped_type dummy;
322363
@@ -386,6 +427,167 @@ ::iterate(
386427 while (!stack.empty ());
387428}
388429
430+ SHARING_MAPT (std::size_t )
431+ ::count_unmarked_nodes(bool leafs_only, std::set<void *> &marked, bool mark)
432+ const
433+ {
434+ if (empty ())
435+ return 0 ;
436+
437+ unsigned count = 0 ;
438+
439+ typedef std::pair<unsigned , const baset *> stack_itemt;
440+
441+ std::stack<stack_itemt> stack;
442+ stack.push ({0 , &map});
443+
444+ do
445+ {
446+ const stack_itemt &si = stack.top ();
447+
448+ const unsigned depth = si.first ;
449+ const baset *bp = si.second ;
450+
451+ stack.pop ();
452+
453+ // internal node or container node
454+ const innert *ip = static_cast <const innert *>(bp);
455+ const unsigned use_count = ip->data .use_count ();
456+ void *raw_ptr = ip->data .get ();
457+
458+ if (use_count >= 2 )
459+ {
460+ if (marked.find (raw_ptr) != marked.end ())
461+ {
462+ continue ;
463+ }
464+
465+ if (mark)
466+ {
467+ marked.insert (raw_ptr);
468+ }
469+ }
470+
471+ if (!leafs_only)
472+ {
473+ count++;
474+ }
475+
476+ if (depth < steps) // internal
477+ {
478+ const to_mapt &m = ip->get_to_map ();
479+ SM_ASSERT (!m.empty ());
480+
481+ for (const auto &item : m)
482+ {
483+ const innert *i = &item.second ;
484+ stack.push ({depth + 1 , i});
485+ }
486+ }
487+ else // container
488+ {
489+ SM_ASSERT (depth == steps);
490+
491+ const leaf_listt &ll = ip->get_container ();
492+ SM_ASSERT (!ll.empty ());
493+
494+ for (const auto &l : ll)
495+ {
496+ const unsigned use_count = l.data .use_count ();
497+ void *raw_ptr = l.data .get ();
498+
499+ if (use_count >= 2 )
500+ {
501+ if (marked.find (raw_ptr) != marked.end ())
502+ {
503+ continue ;
504+ }
505+
506+ if (mark)
507+ {
508+ marked.insert (raw_ptr);
509+ }
510+ }
511+
512+ count++;
513+ }
514+ }
515+ } while (!stack.empty ());
516+
517+ return count;
518+ }
519+
520+ // / Get sharing stats
521+ // /
522+ // / Complexity:
523+ // / - Worst case: O(N * H * log(S))
524+ // / - Best case: O(N + H)
525+ // /
526+ // / \param begin: begin iterator
527+ // / \param end: end iterator
528+ // / \param f: function applied to the iterator to get a sharing map
529+ // / \return: sharing stats
530+ SHARING_MAPT3 (Iterator, , sharing_map_statst)
531+ ::get_sharing_stats(
532+ Iterator begin,
533+ Iterator end,
534+ std::function<sharing_mapt &(const Iterator)> f)
535+ {
536+ std::set<void *> marked;
537+ sharing_map_statst sms;
538+
539+ // We do a separate pass over the tree for each statistic. This is not very
540+ // efficient but the function is intended only for diagnosis purposes anyways.
541+
542+ // number of nodes
543+ for (Iterator it = begin; it != end; it++)
544+ {
545+ sms.num_nodes += f (it).count_unmarked_nodes (false , marked, false );
546+ }
547+
548+ SM_ASSERT (marked.empty ());
549+
550+ // number of unique nodes
551+ for (Iterator it = begin; it != end; it++)
552+ {
553+ sms.num_unique_nodes += f (it).count_unmarked_nodes (false , marked, true );
554+ }
555+
556+ marked.clear ();
557+
558+ // number of leafs
559+ for (Iterator it = begin; it != end; it++)
560+ {
561+ sms.num_leafs += f (it).count_unmarked_nodes (true , marked, false );
562+ }
563+
564+ SM_ASSERT (marked.empty ());
565+
566+ // number of unique leafs
567+ for (Iterator it = begin; it != end; it++)
568+ {
569+ sms.num_unique_leafs += f (it).count_unmarked_nodes (true , marked, true );
570+ }
571+
572+ return sms;
573+ }
574+
575+ // / Get sharing stats
576+ // /
577+ // / Complexity:
578+ // / - Worst case: O(N * H * log(S))
579+ // / - Best case: O(N + H)
580+ // /
581+ // / \param begin: begin iterator of a map
582+ // / \param end: end iterator of a map
583+ // / \return: sharing stats
584+ SHARING_MAPT3 (Iterator, , sharing_map_statst)
585+ ::get_sharing_stats_map(Iterator begin, Iterator end)
586+ {
587+ return get_sharing_stats<Iterator>(
588+ begin, end, [](const Iterator it) -> sharing_mapt & { return it->second ; });
589+ }
590+
389591// / Get a view of the elements in the map
390592// / A view is a list of pairs with the components being const references to the
391593// / keys and values in the map.
0 commit comments