@@ -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,36 @@ 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+ class sharing_map_statst
313+ {
314+ public:
315+ std::size_t num_nodes = 0 ;
316+ std::size_t num_unique_nodes = 0 ;
317+ std::size_t num_leafs = 0 ;
318+ std::size_t num_unique_leafs = 0 ;
319+ };
320+
321+ template <class Iterator >
322+ static sharing_map_statst get_sharing_stats (
323+ Iterator begin,
324+ Iterator end,
325+ std::function<sharing_mapt &(const Iterator)> f =
326+ [](const Iterator it) -> sharing_mapt & { return *it; });
327+
328+ template <class Iterator >
329+ static sharing_map_statst get_sharing_stats_map (Iterator begin, Iterator end);
330+
294331protected:
295332 // helpers
296333
@@ -317,6 +354,11 @@ class sharing_mapt
317354 void gather_all (const baset &n, const unsigned depth, delta_viewt &delta_view)
318355 const ;
319356
357+ std::size_t count_unmarked_nodes (
358+ bool leafs_only,
359+ std::set<void *> &marked,
360+ bool mark = true ) const ;
361+
320362 // dummy element returned when no element was found
321363 static mapped_type dummy;
322364
@@ -386,6 +428,167 @@ ::iterate(
386428 while (!stack.empty ());
387429}
388430
431+ SHARING_MAPT (std::size_t )
432+ ::count_unmarked_nodes(bool leafs_only, std::set<void *> &marked, bool mark)
433+ const
434+ {
435+ if (empty ())
436+ return 0 ;
437+
438+ unsigned count = 0 ;
439+
440+ typedef std::pair<unsigned , const baset *> stack_itemt;
441+
442+ std::stack<stack_itemt> stack;
443+ stack.push ({0 , &map});
444+
445+ do
446+ {
447+ const stack_itemt &si = stack.top ();
448+
449+ const unsigned depth = si.first ;
450+ const baset *bp = si.second ;
451+
452+ stack.pop ();
453+
454+ // internal node or container node
455+ const innert *ip = static_cast <const innert *>(bp);
456+ const unsigned use_count = ip->data .use_count ();
457+ void *raw_ptr = ip->data .get ();
458+
459+ if (use_count >= 2 )
460+ {
461+ if (marked.find (raw_ptr) != marked.end ())
462+ {
463+ continue ;
464+ }
465+
466+ if (mark)
467+ {
468+ marked.insert (raw_ptr);
469+ }
470+ }
471+
472+ if (!leafs_only)
473+ {
474+ count++;
475+ }
476+
477+ if (depth < steps) // internal
478+ {
479+ const to_mapt &m = ip->get_to_map ();
480+ SM_ASSERT (!m.empty ());
481+
482+ for (const auto &item : m)
483+ {
484+ const innert *i = &item.second ;
485+ stack.push ({depth + 1 , i});
486+ }
487+ }
488+ else // container
489+ {
490+ SM_ASSERT (depth == steps);
491+
492+ const leaf_listt &ll = ip->get_container ();
493+ SM_ASSERT (!ll.empty ());
494+
495+ for (const auto &l : ll)
496+ {
497+ const unsigned use_count = l.data .use_count ();
498+ void *raw_ptr = l.data .get ();
499+
500+ if (use_count >= 2 )
501+ {
502+ if (marked.find (raw_ptr) != marked.end ())
503+ {
504+ continue ;
505+ }
506+
507+ if (mark)
508+ {
509+ marked.insert (raw_ptr);
510+ }
511+ }
512+
513+ count++;
514+ }
515+ }
516+ } while (!stack.empty ());
517+
518+ return count;
519+ }
520+
521+ // / Get sharing stats
522+ // /
523+ // / Complexity:
524+ // / - Worst case: O(N * H * log(S))
525+ // / - Best case: O(N + H)
526+ // /
527+ // / \param begin: begin iterator
528+ // / \param end: end iterator
529+ // / \param f: function applied to the iterator to get a sharing map
530+ // / \return: sharing stats
531+ SHARING_MAPT3 (Iterator, , sharing_map_statst)
532+ ::get_sharing_stats(
533+ Iterator begin,
534+ Iterator end,
535+ std::function<sharing_mapt &(const Iterator)> f)
536+ {
537+ std::set<void *> marked;
538+ sharing_map_statst sms;
539+
540+ // We do a separate pass over the tree for each statistic. This is not very
541+ // efficient but the function is intended only for diagnosis purposes anyways.
542+
543+ // number of nodes
544+ for (Iterator it = begin; it != end; it++)
545+ {
546+ sms.num_nodes += f (it).count_unmarked_nodes (false , marked, false );
547+ }
548+
549+ SM_ASSERT (marked.empty ());
550+
551+ // number of unique nodes
552+ for (Iterator it = begin; it != end; it++)
553+ {
554+ sms.num_unique_nodes += f (it).count_unmarked_nodes (false , marked, true );
555+ }
556+
557+ marked.clear ();
558+
559+ // number of leafs
560+ for (Iterator it = begin; it != end; it++)
561+ {
562+ sms.num_leafs += f (it).count_unmarked_nodes (true , marked, false );
563+ }
564+
565+ SM_ASSERT (marked.empty ());
566+
567+ // number of unique leafs
568+ for (Iterator it = begin; it != end; it++)
569+ {
570+ sms.num_unique_leafs += f (it).count_unmarked_nodes (true , marked, true );
571+ }
572+
573+ return sms;
574+ }
575+
576+ // / Get sharing stats
577+ // /
578+ // / Complexity:
579+ // / - Worst case: O(N * H * log(S))
580+ // / - Best case: O(N + H)
581+ // /
582+ // / \param begin: begin iterator of a map
583+ // / \param end: end iterator of a map
584+ // / \return: sharing stats
585+ SHARING_MAPT3 (Iterator, , sharing_map_statst)
586+ ::get_sharing_stats_map(Iterator begin, Iterator end)
587+ {
588+ return get_sharing_stats<Iterator>(
589+ begin, end, [](const Iterator it) -> sharing_mapt & { return it->second ; });
590+ }
591+
389592// / Get a view of the elements in the map
390593// / A view is a list of pairs with the components being const references to the
391594// / keys and values in the map.
0 commit comments