32#include "timemory/backends/dmp.hpp"
33#include "timemory/backends/gperftools.hpp"
34#include "timemory/backends/threading.hpp"
48#include "timemory/tpls/cereal/cereal.hpp"
59#include <unordered_map>
60#include <unordered_set>
68TIMEMORY_NOINLINE storage_singleton<Tp>*
78 using component_type =
typename Tp::component_type;
80 ? std::make_unique<singleton_type>()
81 : std::unique_ptr<singleton_type>{};
83 return _instance.get();
98template <
typename Type>
99class storage<Type,
true> :
public base::storage
104 static constexpr bool has_data_v =
true;
106 template <
typename KeyT,
typename MappedT>
107 using uomap_t = std::unordered_map<KeyT, MappedT>;
109 using result_node = node::result<Type>;
110 using graph_node = node::graph<Type>;
111 using strvector_t = std::vector<string_t>;
112 using uintvector_t = std::vector<uint64_t>;
113 using EmptyT = std::tuple<>;
114 using base_type = base::storage;
115 using component_type = Type;
116 using this_type = storage<Type, has_data_v>;
117 using smart_pointer = std::unique_ptr<this_type, impl::storage_deleter<this_type>>;
118 using singleton_t = singleton<this_type, smart_pointer>;
119 using singleton_type = singleton_t;
120 using pointer =
typename singleton_t::pointer;
125 using result_array_t = std::vector<result_node>;
126 using dmp_result_t = std::vector<result_array_t>;
127 using printer_t = operation::finalize::print<Type, has_data_v>;
128 using sample_array_t = std::vector<Type>;
129 using graph_node_t = graph_node;
130 using graph_data_t = graph_data<graph_node_t>;
131 using graph_t =
typename graph_data_t::graph_t;
132 using graph_type = graph_t;
133 using iterator =
typename graph_type::iterator;
134 using const_iterator =
typename graph_type::const_iterator;
136 template <
typename Vp>
137 using secondary_data_t = std::tuple<iterator, const std::string&, Vp>;
138 using iterator_hash_submap_t = uomap_t<int64_t, iterator>;
139 using iterator_hash_map_t = uomap_t<int64_t, iterator_hash_submap_t>;
142 friend struct node::result<Type>;
143 friend struct node::graph<Type>;
144 friend struct impl::storage_deleter<this_type>;
146 friend struct operation::finalize::mpi_get<Type, has_data_v>;
147 friend struct operation::finalize::upc_get<Type, has_data_v>;
148 friend struct operation::finalize::dmp_get<Type, has_data_v>;
150 friend struct operation::finalize::merge<Type, has_data_v>;
154 static pointer instance();
155 static pointer master_instance();
156 static pointer noninit_instance();
157 static pointer noninit_master_instance();
159 static bool& master_is_finalizing();
160 static bool& worker_is_finalizing();
161 static bool is_finalizing();
164 static singleton_t* get_singleton() {
return get_storage_singleton<this_type>(); }
165 static std::atomic<int64_t>& instance_count();
172 storage(
const this_type&) =
delete;
173 storage(this_type&&) =
delete;
175 this_type& operator=(
const this_type&) =
delete;
176 this_type& operator=(this_type&& rhs) =
delete;
179 void get_shared_manager();
181 void print() final { internal_print(); }
182 void cleanup() final { operation::cleanup<Type>{}; }
186 void stack_clear() final;
187 bool global_init() final;
188 bool thread_init() final;
189 bool data_init() final;
191 const graph_data_t& data() const;
192 const graph_t& graph() const;
193 int64_t depth() const;
194 graph_data_t& data();
199 inline
bool empty()
const
201 return (m_graph_data_instance) ? (_data().graph().size() <= 1) :
true;
203 inline size_t size()
const
205 return (m_graph_data_instance) ? (_data().graph().size() - 1) : 0;
207 inline size_t true_size()
const
209 if(!m_graph_data_instance)
211 size_t _sz = _data().graph().size();
212 size_t _dc = _data().dummy_count();
213 return (_dc < _sz) ? (_sz - _dc) : 0;
216 result_array_t
get();
217 dmp_result_t mpi_get();
218 dmp_result_t upc_get();
219 dmp_result_t dmp_get();
221 template <
typename Tp>
223 template <
typename Tp>
225 template <
typename Tp>
227 template <
typename Tp>
230 std::shared_ptr<printer_t> get_printer()
const {
return m_printer; }
232 iterator_hash_map_t get_node_ids()
const {
return m_node_ids; }
234 void stack_push(Type* obj) { m_stack.insert(obj); }
235 void stack_pop(Type* obj);
239 iterator
insert(scope::config scope_data,
const Type& obj, uint64_t hash_id);
242 template <
typename Vp, enable_if_t<!std::is_same<decay_t<Vp>, Type>::value,
int> = 0>
243 iterator append(
const secondary_data_t<Vp>& _secondary);
246 template <
typename Vp, enable_if_t<std::is_same<decay_t<Vp>, Type>::value,
int> = 0>
247 iterator append(
const secondary_data_t<Vp>& _secondary);
249 template <
typename Archive>
250 void serialize(Archive& ar,
unsigned int version);
252 void add_sample(Type&& _obj) { m_samples.emplace_back(std::forward<Type>(_obj)); }
254 auto& get_samples() {
return m_samples; }
255 const auto& get_samples()
const {
return m_samples; }
258 iterator insert_tree(uint64_t hash_id,
const Type& obj, uint64_t hash_depth);
259 iterator insert_timeline(uint64_t hash_id,
const Type& obj, uint64_t hash_depth);
260 iterator insert_flat(uint64_t hash_id,
const Type& obj, uint64_t hash_depth);
261 iterator insert_hierarchy(uint64_t hash_id,
const Type& obj, uint64_t hash_depth,
265 void merge(this_type* itr);
266 string_t get_prefix(
const graph_node&);
267 string_t get_prefix(iterator _node) {
return get_prefix(*_node); }
268 string_t get_prefix(
const uint64_t& _id);
271 void check_consistency();
273 template <
typename Archive>
274 void do_serialize(Archive& ar);
276 void internal_print();
278 graph_data_t& _data();
279 const graph_data_t& _data()
const
282 return const_cast<type_t*
>(
this)->_data();
286 uint64_t m_timeline_counter = 1;
287 mutable graph_data_t* m_graph_data_instance =
nullptr;
288 iterator_hash_map_t m_node_ids;
289 std::unordered_set<Type*> m_stack;
290 std::shared_ptr<printer_t> m_printer;
291 sample_array_t m_samples;
296template <
typename Type>
301 if(m_graph_data_instance)
302 m_graph_data_instance->reset();
304 for(
auto& ditr : m_node_ids)
306 auto _depth = ditr.first;
313 for(
auto itr = ditr.second.begin(); itr != ditr.second.end(); ++itr)
316 ditr.second.erase(itr);
324template <
typename Type>
330 using force_tree_t = trait::tree_storage<Type>;
331 using force_flat_t = trait::flat_storage<Type>;
332 using force_time_t = trait::timeline_storage<Type>;
338 if(!m_is_master && _data().at_sea_level() &&
339 _data().dummy_count() < m_settings->get_max_thread_bookmarks())
343 auto hash_depth = scope_data.compute_depth<force_tree_t, force_flat_t, force_time_t>(
347 auto hash_value = scope_data.compute_hash<force_tree_t, force_flat_t, force_time_t>(
348 hash_id, hash_depth, m_timeline_counter);
355 if(scope_data.is_flat() || force_flat_t::value)
356 return insert_flat(hash_value, obj, hash_depth);
366 return insert_tree(hash_value, obj, hash_depth);
371template <
typename Type>
372template <
typename Vp, enable_if_t<!std::is_same<decay_t<Vp>, Type>::value,
int>>
374storage<Type, true>::append(
const secondary_data_t<Vp>& _secondary)
379 auto&& _itr = std::get<0>(_secondary);
380 if(!_data().graph().is_valid(_itr))
391 auto _depth = _itr->depth() + 1;
394 auto _nitr = m_node_ids[_depth].find(_hash);
395 if(_nitr != m_node_ids[_depth].
end())
398 auto& _obj = _nitr->second->obj();
399 _obj += std::get<2>(_secondary);
400 _obj.set_laps(_nitr->second->obj().get_laps() + 1);
401 auto& _stats = _nitr->second->stats();
402 operation::add_statistics<Type>(_nitr->second->obj(), _stats);
403 return _nitr->second;
407 auto&& _tmp = Type{};
408 _tmp += std::get<2>(_secondary);
409 _tmp.set_laps(_tmp.get_laps() + 1);
410 graph_node_t _node{ _hash, _tmp, _depth, m_thread_idx };
411 _node.stats() += _tmp.get();
412 auto& _stats = _node.stats();
413 operation::add_statistics<Type>(_tmp, _stats);
414 auto itr = _data().emplace_child(_itr, std::move(_node));
415 operation::set_iterator<Type>{}(itr->data(), itr);
416 m_node_ids[_depth][_hash] = itr;
422template <
typename Type>
423template <
typename Vp, enable_if_t<std::is_same<decay_t<Vp>, Type>::value,
int>>
425storage<Type, true>::append(
const secondary_data_t<Vp>& _secondary)
430 auto&& _itr = std::get<0>(_secondary);
431 if(!_data().graph().is_valid(_itr))
442 auto _depth = _itr->depth() + 1;
445 auto _nitr = m_node_ids[_depth].find(_hash);
446 if(_nitr != m_node_ids[_depth].
end())
448 _nitr->second->obj() += std::get<2>(_secondary);
449 return _nitr->second;
453 auto&& _tmp = std::get<2>(_secondary);
454 auto itr = _data().emplace_child(
455 _itr, graph_node_t{ _hash, _tmp,
static_cast<int64_t
>(_depth), m_thread_idx });
456 operation::set_iterator<Type>{}(itr->data(), itr);
457 m_node_ids[_depth][_hash] = itr;
463template <
typename Type>
465storage<Type, true>::insert_tree(uint64_t hash_id,
const Type& obj, uint64_t hash_depth)
467 bool has_head = _data().has_head();
468 return insert_hierarchy(hash_id, obj, hash_depth, has_head);
473template <
typename Type>
475storage<Type, true>::insert_timeline(uint64_t hash_id,
const Type& obj,
478 auto _current = _data().current();
479 return _data().emplace_child(
481 graph_node_t{ hash_id, obj,
static_cast<int64_t
>(hash_depth), m_thread_idx });
486template <
typename Type>
488storage<Type, true>::insert_flat(uint64_t hash_id,
const Type& obj, uint64_t hash_depth)
490 static thread_local auto _current = _data().head();
491 static thread_local bool _first =
true;
497 _current = _current.begin();
501 auto itr = _data().emplace_child(
502 _current, graph_node_t{ hash_id, obj,
static_cast<int64_t
>(hash_depth),
504 m_node_ids[hash_depth][hash_id] = itr;
510 auto _existing = m_node_ids[hash_depth].find(hash_id);
511 if(_existing != m_node_ids[hash_depth].
end())
512 return m_node_ids[hash_depth].find(hash_id)->second;
514 auto itr = _data().emplace_child(
516 graph_node_t{ hash_id, obj,
static_cast<int64_t
>(hash_depth), m_thread_idx });
517 m_node_ids[hash_depth][hash_id] = itr;
523template <
typename Type>
525storage<Type, true>::insert_hierarchy(uint64_t hash_id,
const Type& obj,
526 uint64_t hash_depth,
bool has_head)
528 using id_hash_map_t =
typename iterator_hash_map_t::mapped_type;
530 auto& m_data = m_graph_data_instance;
531 auto tid = m_thread_idx;
534 if(!has_head || (m_is_master && m_node_ids.empty()))
536 auto itr = m_data->append_child(
537 graph_node_t{ hash_id, obj,
static_cast<int64_t
>(hash_depth), tid });
538 m_node_ids[hash_depth][hash_id] = itr;
543 auto _update = [&](iterator itr) {
544 m_data->depth() = itr->depth();
545 return (m_data->current() = itr);
548 if(m_node_ids[hash_depth].find(hash_id) != m_node_ids[hash_depth].end() &&
549 m_node_ids[hash_depth].find(hash_id)->second->depth() == m_data->depth())
551 return _update(m_node_ids[hash_depth].find(hash_id)->second);
554 using sibling_itr =
typename graph_t::sibling_iterator;
555 graph_node_t node{ hash_id, obj, m_data->depth(), tid };
558 auto _insert_child = [&]() {
559 node.depth() = hash_depth;
560 auto itr = m_data->append_child(std::move(node));
561 auto ditr = m_node_ids.find(hash_depth);
562 if(ditr == m_node_ids.end())
563 m_node_ids.insert({ hash_depth, id_hash_map_t{} });
564 auto hitr = m_node_ids.at(hash_depth).find(hash_id);
565 if(hitr == m_node_ids.at(hash_depth).end())
566 m_node_ids.at(hash_depth).insert({ hash_id, iterator{} });
567 m_node_ids.at(hash_depth).at(hash_id) = itr;
571 auto current = m_data->current();
572 if(!m_data->graph().is_valid(current))
576 auto fchild = graph_t::child(current, 0);
577 if(m_data->graph().is_valid(fchild))
579 for(sibling_itr itr = fchild.begin(); itr != fchild.end(); ++itr)
581 if((hash_id) == itr->id())
590 if((hash_id) == current->id())
594 for(sibling_itr itr = current.begin(); itr != current.end(); ++itr)
600 if((hash_id) == itr->id())
604 return _insert_child();
610template <
typename Type>
611template <
typename Archive>
615 auto&& _results = dmp_get();
616 operation::serialization<Type>{}(ar, _results);
622template <
typename Type>
623template <
typename Archive>
625storage<Type, true>::do_serialize(Archive& ar)
630 auto&& _results = dmp_get();
631 operation::serialization<Type>{}(ar, _results);
636template <
typename Type>
637typename storage<Type, true>::pointer
638storage<Type, true>::instance()
640 return get_singleton() ? get_singleton()->instance() :
nullptr;
645template <
typename Type>
646typename storage<Type, true>::pointer
647storage<Type, true>::master_instance()
649 return get_singleton() ? get_singleton()->master_instance() :
nullptr;
659template <
typename Type>
660class storage<Type,
false> :
public base::storage
665 static constexpr bool has_data_v =
false;
667 using result_node = std::tuple<>;
668 using graph_node = std::tuple<>;
669 using graph_t = std::tuple<>;
670 using graph_type = graph_t;
671 using dmp_result_t = std::vector<std::tuple<>>;
672 using result_array_t = std::vector<std::tuple<>>;
673 using uintvector_t = std::vector<uint64_t>;
674 using base_type = base::storage;
675 using component_type = Type;
676 using this_type = storage<Type, has_data_v>;
678 using smart_pointer = std::unique_ptr<this_type, impl::storage_deleter<this_type>>;
679 using singleton_t = singleton<this_type, smart_pointer>;
680 using singleton_type = singleton_t;
681 using pointer =
typename singleton_t::pointer;
683 using printer_t = operation::finalize::print<Type, has_data_v>;
685 using iterator =
void*;
686 using const_iterator =
const void*;
689 friend struct node::result<Type>;
690 friend struct node::graph<Type>;
691 friend struct impl::storage_deleter<this_type>;
693 friend struct operation::finalize::mpi_get<Type, has_data_v>;
694 friend struct operation::finalize::upc_get<Type, has_data_v>;
695 friend struct operation::finalize::dmp_get<Type, has_data_v>;
697 friend struct operation::finalize::merge<Type, has_data_v>;
700 static pointer instance();
701 static pointer master_instance();
702 static pointer noninit_instance();
703 static pointer noninit_master_instance();
705 static bool& master_is_finalizing();
706 static bool& worker_is_finalizing();
707 static bool is_finalizing();
710 static singleton_t* get_singleton() {
return get_storage_singleton<this_type>(); }
711 static std::atomic<int64_t>& instance_count();
717 explicit storage(
const this_type&) =
delete;
718 explicit storage(this_type&&) =
delete;
719 this_type& operator=(
const this_type&) =
delete;
720 this_type& operator=(this_type&& rhs) =
delete;
723 void cleanup() final { operation::cleanup<Type>{}; }
724 void stack_clear() final;
725 void disable() final { trait::runtime_enabled<component_type>::set(
false); }
731 TIMEMORY_NODISCARD
bool empty()
const {
return true; }
732 TIMEMORY_NODISCARD
inline size_t size()
const {
return 0; }
733 TIMEMORY_NODISCARD
inline size_t true_size()
const {
return 0; }
734 TIMEMORY_NODISCARD
inline size_t depth()
const {
return 0; }
736 iterator
pop() {
return nullptr; }
737 iterator
insert(int64_t,
const Type&,
const string_t&) {
return nullptr; }
739 template <
typename Archive>
740 void serialize(Archive&,
const unsigned int)
743 void stack_push(Type* obj) { m_stack.insert(obj); }
744 void stack_pop(Type* obj);
746 TIMEMORY_NODISCARD std::shared_ptr<printer_t> get_printer()
const
752 void get_shared_manager();
754 void merge(this_type* itr);
757 template <
typename Archive>
758 void do_serialize(Archive&)
762 std::unordered_set<Type*> m_stack;
763 std::shared_ptr<printer_t> m_printer;
768template <
typename Type>
769typename storage<Type, false>::pointer
770storage<Type, false>::instance()
772 return get_singleton() ? get_singleton()->instance() :
nullptr;
777template <
typename Type>
778typename storage<Type, false>::pointer
779storage<Type, false>::master_instance()
781 return get_singleton() ? get_singleton()->master_instance() :
nullptr;
797template <
typename Tp,
typename Vp>
798class storage final :
public impl::storage<Tp, trait::uses_value_storage<Tp, Vp>::value>
803 using base_type = impl::storage<Tp, uses_value_storage_v>;
812 friend struct impl::storage_deleter<
this_type>;
817 using base_type::instance;
820 using base_type::master_instance;
823 using base_type::noninit_instance;
826 using base_type::noninit_master_instance;
828 using base_type::master_is_finalizing;
830 using base_type::worker_is_finalizing;
832 using base_type::is_finalizing;
836 using base_type::empty;
838 using base_type::size;
840 using base_type::true_size;
844 using base_type::depth;
851 using base_type::stack_pop;
854 using base_type::stack_push;
859template <
typename Tp>
862 Tp, conditional_t<trait::is_available<Tp>::value, typename Tp::value_type, void>>
869 using base_type = impl::storage<Tp, uses_value_storage_v>;
878 friend struct impl::storage_deleter<
this_type>;
889template <
typename StorageType>
890struct storage_deleter :
public std::default_delete<StorageType>
892 using Pointer = std::unique_ptr<StorageType, storage_deleter<StorageType>>;
895 storage_deleter() =
default;
896 ~storage_deleter() =
default;
898 void operator()(StorageType* ptr)
903 StorageType* master = singleton_t::master_instance_ptr();
904 std::thread::id master_tid = singleton_t::master_thread_id();
905 std::thread::id this_tid = std::this_thread::get_id();
907 static_assert(!std::is_same<StorageType, tim::base::storage>::value,
908 "Error! Base class");
911 if(ptr && master && ptr != master)
913 ptr->StorageType::stack_clear();
914 master->StorageType::merge(ptr);
921 if(ptr && !master && this_tid != master_tid)
923 ptr->StorageType::free_shared_manager();
930 ptr->StorageType::print();
936 master->StorageType::stack_clear();
937 master->StorageType::print();
938 master->StorageType::cleanup();
939 _printed_master =
true;
944 if(this_tid == master_tid)
949 ptr->StorageType::free_shared_manager();
956 if(master && ptr != master)
957 singleton_t::remove(ptr);
960 ptr->StorageType::free_shared_manager();
965 if(_printed_master && !_deleted_master)
970 master->StorageType::free_shared_manager();
974 _deleted_master =
true;
977 using Type =
typename StorageType::component_type;
979 trait::runtime_enabled<Type>::set(
false);
982 bool _printed_master =
false;
983 bool _deleted_master =
false;
992template <
typename Tp,
typename Vp>
994base::storage::base_instance()
Thread-safe singleton management.
typename base_type::const_iterator const_iterator
conditional_t< trait::is_available< Tp >::value, typename Tp::value_type, void > Vp
typename singleton_t::auto_lock_t auto_lock_t
typename base_type::iterator iterator
std::unique_ptr< base_type, deleter_t > smart_pointer
impl::storage_deleter< base_type > deleter_t
impl::storage< Tp, uses_value_storage_v > base_type
typename singleton_t::pointer pointer
typename singleton_t::auto_lock_t auto_lock_t
impl::storage_deleter< base_type > deleter_t
impl::storage< Tp, uses_value_storage_v > base_type
typename base_type::const_iterator const_iterator
typename singleton_t::pointer pointer
std::unique_ptr< base_type, deleter_t > smart_pointer
typename base_type::iterator iterator
The declaration for the types for manager without definitions.
Responsible for maintaining the call-stack storage in timemory. This class and the serialization libr...
void serialize(std::string fname, exec_data< Counter > &obj)
hash_value_t add_hash_id(hash_map_ptr_t &_hash_map, string_view_cref_t _prefix)
add an string to the given hash-map (if it doesn't already exist) and return the hash
const hash_alias_ptr_t hash_value_t std::string *& _ret
_reported insert(_hash_id)
void pop(TupleT< Tp... > &obj, Args &&... args)
void reset(TupleT< Tp... > &obj, Args &&... args)
void print(std::ostream &os, Args &&... args)
std::tuple< bool, uint32_t, uint32_t, uint64_t, int64_t, Tp, stats_type > node_type
typename stats_policy::statistics_type stats_type
std::tuple< uint32_t, uint32_t, int64_t, uint64_t, uint64_t, string_t, uintvector_t, Tp, stats_type > result_type
std::unordered_map< KeyT, MappedT > uomap_t
storage_singleton< Tp > * get_storage_singleton()
std::unique_lock< mutex_t > auto_lock_t
Unique lock type around mutex_t.
void initialize(CompList< CompTypes... > &obj, std::initializer_list< EnumT > components)
typename std::remove_pointer< U >::type remove_pointer_t
typename std::decay< T >::type decay_t
Alias template for decay.
tim::mpl::apply< std::string > string
auto get(const auto_bundle< Tag, Types... > &_obj)
typename std::conditional< B, Lhs, Rhs >::type conditional_t
void consume_parameters(ArgsT &&...)
lightweight tuple-alternative for meta-programming logic
Declare the operations types.
Include the macros for storage.
Declare the storage types.
provides an object which can be returned from functions that will execute the lambda provided during ...
trait that signifies that an implementation is enabled at runtime. The value returned from get() is f...
static bool get(enable_if_t< is_available< U >::value &&get_value< U >(), int >=0)
GET specialization if component is available.
static bool set(bool val, enable_if_t< is_available< U >::value &&get_value< U >(), int >=0)
SET specialization if component is available.
This trait is used to determine whether the (expensive) instantiation of the storage class happens.