24 #include <libxml/xmlwriter.h> 31 #include <string_view> 37 const std::array<std::string, num_timer_levels> timer_level_names = {
"none",
"coarse",
"medium",
"fine"};
39 const char TIMER_STACK_SEPARATOR =
'/';
41 std::unique_ptr<TimerManager<NewTimer>> global_timer_manager;
46 if (!global_timer_manager)
47 global_timer_manager = std::make_unique<TimerManager<NewTimer>>();
48 return *global_timer_manager;
59 if (t.get_name().find(TIMER_STACK_SEPARATOR) != std::string::npos)
60 app_log() <<
"Warning: Timer name (" << t.get_name() <<
") should not contain the character " 61 << TIMER_STACK_SEPARATOR << std::endl;
63 if (timer_name_to_id.find(t.get_name()) == timer_name_to_id.end())
65 t.set_id(max_timer_id);
66 timer_id_name[t.get_id()] = t.get_name();
67 timer_name_to_id[t.get_name()] = t.get_id();
68 if (max_timer_id >= std::numeric_limits<timer_id_t>::max())
70 max_timers_exceeded =
true;
71 app_log() <<
"Number of timers exceeds limit (" <<
static_cast<int>(std::numeric_limits<timer_id_t>::max())
72 <<
"). Adjust timer_id_t in NewTimer.h and recompile." << std::endl;
78 t.set_id(timer_name_to_id[t.get_name()]);
80 t.set_active_by_timer_threshold(timer_threshold);
88 const std::lock_guard<std::mutex> lock(timer_list_lock_);
89 timer_storage_.push_back(std::make_unique<TIMER>(myname,
this, mytimer));
90 t = timer_storage_.back().get();
100 if (t == current_timer())
102 std::cerr <<
"Timer " << t->get_name()
103 <<
" instance is already at the top of the stack. " 104 "start() is being called again. This often happens when stop() is not paired properly with start(). " 105 <<
"ScopedTimer uses RAII and manages timer start/stop more safely." << std::endl;
106 throw std::runtime_error(
"TimerManager push_timer error!");
109 CurrentTimerStack.push_back(t);
112 template<
class TIMER>
115 TIMER* stack_top = current_timer();
116 if (stack_top ==
nullptr)
118 std::cerr <<
"Timer stack pop failed on an empty stack! Requested \"" << t->get_name() <<
"\"." << std::endl;
119 throw std::runtime_error(
"TimerManager pop_timer error!");
121 else if (t != stack_top)
123 std::cerr <<
"Timer stack pop not matching push! " 124 <<
"Expecting \"" << t->get_name() <<
"\" but \"" << stack_top->get_name() <<
"\" is on the top." 126 throw std::runtime_error(
"TimerManager pop_timer error!");
129 CurrentTimerStack.pop_back();
132 template<
class TIMER>
135 for (
int i = 0; i < timer_storage_.size(); i++)
136 timer_storage_[i]->reset();
139 template<
class TIMER>
142 timer_threshold = threshold;
143 for (
int i = 0; i < timer_storage_.size(); i++)
144 timer_storage_[i]->set_active_by_timer_threshold(timer_threshold);
147 template<
class TIMER>
150 const auto it = std::find(timer_level_names.begin(), timer_level_names.end(), threshold);
151 if (it != timer_level_names.end())
152 set_timer_threshold(static_cast<timer_levels>(std::distance(timer_level_names.begin(), it)));
155 std::cerr <<
"Unknown timer level: " << threshold <<
" , current level: " << timer_level_names[timer_threshold]
160 template<
class TIMER>
163 return timer_level_names[timer_threshold];
167 template<
class TIMER>
170 for (
int i = 0; i < timer_storage_.size(); ++i)
172 TIMER& timer = *timer_storage_[i];
173 nameList_t::iterator it(p.nameList.find(timer.get_name()));
174 if (it == p.nameList.end())
176 int ind = p.nameList.size();
177 p.nameList[timer.get_name()] = ind;
178 p.timeList.push_back(timer.get_total());
179 p.callList.push_back(timer.get_num_calls());
183 int ind = (*it).second;
184 p.timeList[ind] += timer.get_total();
185 p.callList[ind] += timer.get_num_calls();
212 for (
int i = 0; i < stack_name.length(); i++)
213 if (stack_name[i] == TIMER_STACK_SEPARATOR)
221 int pos = stack_name.find_last_of(TIMER_STACK_SEPARATOR);
222 if (pos == std::string::npos)
225 return stack_name.substr(pos + 1, stack_name.length() - pos);
228 template<
class TIMER>
233 std::string& timer_name = timer_id_name[key.
get_id(i)];
237 stack_name += TIMER_STACK_SEPARATOR;
239 stack_name += timer_name;
243 template<
class TIMER>
246 #ifdef USE_STACK_TIMERS 252 std::map<std::string, ProfileData> all_stacks;
253 for (
int i = 0; i < timer_storage_.size(); ++i)
255 TIMER& timer = *timer_storage_[i];
256 for (
const auto& [key, time] : timer.get_per_stack_total_time())
259 std::string stack_name;
260 get_stack_name_from_id(key, stack_name);
261 pd.
time = timer.get_total(key);
262 pd.
calls = timer.get_num_calls(key);
264 all_stacks[stack_name] += pd;
270 for (
const auto& [stack_name, data] : all_stacks)
272 p.nameList[stack_name] = idx;
273 p.names.push_back(stack_name);
274 p.timeList.push_back(data.time);
275 p.timeExclList.push_back(data.time);
276 p.callList.push_back(data.calls);
281 for (idx = 0; idx < p.timeList.size(); idx++)
283 int start_level =
get_level(p.names[idx]);
284 for (
int i = idx + 1; i < p.timeList.size(); i++)
287 if (level == start_level + 1)
288 p.timeExclList[idx] -= p.timeExclList[i];
290 if (level == start_level)
297 template<
class TIMER>
304 app_log() <<
"Use --enable-timers=<value> command line option to increase or decrease level of timing information" 306 #ifdef USE_STACK_TIMERS 308 app_log() <<
"Stack timer profile" << std::endl;
312 app_log() <<
"\nFlat profile" << std::endl;
318 template<
class TIMER>
324 collate_flat_profile(
comm, p);
330 std::array<char, 256> tmpout;
331 std::map<std::string, int>::iterator it(p.nameList.begin()), it_end(p.nameList.end());
334 int i = (*it).second;
336 std::snprintf(tmpout.data(), tmpout.size(),
"%-40s %9.4f %13ld %16.9f %12.6f TIMER\n",
337 (*it).first.c_str(), p.timeList[i], p.callList[i],
338 p.timeList[i] / (
static_cast<double>(p.callList[i]) + std::numeric_limits<double>::epsilon()),
341 throw std::runtime_error(
"Error generating timer string");
342 app_log() << std::string_view(tmpout.data(), length);
351 void pad_string(
const std::string& in, std::string& out,
int field_len)
354 int pad_len = std::max(field_len - len, 0);
355 std::string pad_str(pad_len,
' ');
359 template<
class TIMER>
365 collate_stack_profile(
comm, p);
373 app_warning() <<
"Adjust StackKey in NewTimer.h and recompile." << std::endl;
377 int max_name_len = 0;
378 for (
int i = 0; i < p.names.size(); i++)
380 std::string stack_name = p.names[i];
383 int name_len = name.size() + indent_len * level;
384 max_name_len = std::max(name_len, max_name_len);
387 std::array<char, 256> tmpout;
388 std::string timer_name;
389 pad_string(
"Timer", timer_name, max_name_len);
391 int length = std::snprintf(tmpout.data(), tmpout.size(),
"%s %-9s %-9s %-10s %-13s\n", timer_name.c_str(),
392 "Inclusive_time",
"Exclusive_time",
"Calls",
"Time_per_call");
394 throw std::runtime_error(
"Error generating timer string");
395 app_log() << std::string_view(tmpout.data(), length);
397 for (
int i = 0; i < p.names.size(); i++)
399 std::string stack_name = p.names[i];
402 std::string indent_str(indent_len * level,
' ');
403 std::string indented_str = indent_str + name;
404 std::string padded_name_str;
405 pad_string(indented_str, padded_name_str, max_name_len);
407 std::snprintf(tmpout.data(), tmpout.size(),
"%s %9.4f %9.4f %13ld %16.9f\n", padded_name_str.c_str(),
408 p.timeList[i], p.timeExclList[i], p.callList[i],
409 p.timeList[i] / (
static_cast<double>(p.callList[i]) + std::numeric_limits<double>::epsilon()));
411 throw std::runtime_error(
"Error generating timer string");
412 app_log() << std::string_view(tmpout.data(), length);
418 template<
class TIMER>
421 #if defined(ENABLE_TIMERS) && defined(USE_STACK_TIMERS) 424 collate_stack_profile(
comm, p);
428 xmlNodePtr timing_root =
doc.
addChild(root,
"timing");
430 doc.
addChild(timing_root,
"max_timers_exceeded", max_timers_exceeded ?
"yes" :
"no");
431 std::vector<xmlNodePtr> node_stack;
432 node_stack.push_back(timing_root);
433 xmlNodePtr current_root = timing_root;
435 for (
int i = 0; i < p.names.size(); i++)
437 std::string stack_name = p.names[i];
441 std::string indent_str(2 * level,
' ');
443 xmlNodePtr timer =
doc.
addChild(current_root,
"timer");
446 doc.
addChild(timer,
"time_excl", p.timeExclList[i]);
449 int next_level = level;
450 if (i + 1 < p.names.size())
453 if (next_level > level)
455 xmlNodePtr next_node =
doc.
addChild(timer,
"includes");
456 node_stack.push_back(next_node);
457 current_root = next_node;
459 if (next_level < level)
460 for (
int j = 0; j < level - next_level; j++)
462 node_stack.pop_back();
463 current_root = node_stack.back();
class that handles xmlDoc
timer_id_t get_id(int idx) const
std::ostream & app_warning()
helper functions for EinsplineSetBuilder
int rank() const
return the rank
std::string get_leaf_name(const std::string &stack_name)
void output_timing(Communicate *comm, Libxml2Document &doc, xmlNodePtr root)
void collate_stack_profile(Communicate *comm, StackProfileData &p)
void print_flat(Communicate *comm)
void initializeTimer(TIMER &t)
Timer accumulates time and call counts.
Manager creates timers and handle reports.
void push_timer(TIMER *t)
int size() const
return the number of tasks
ProfileData & operator+=(const ProfileData &pd)
void print(Communicate *comm)
int get_level(const std::string &stack_name)
Wrapping information on parallelism.
omp_int_t omp_get_max_threads()
NewTimer & createGlobalTimer(const std::string &myname, timer_levels mylevel)
void collate_flat_profile(Communicate *comm, FlatProfileData &p)
bool timer_max_level_exceeded
TIMER * createTimer(const std::string &myname, timer_levels mytimer=timer_level_fine)
Create a new timer object registred in this manager. This call is thread-safe.
void pad_string(const std::string &in, std::string &out, int field_len)
void addChild(xmlNodePtr newnode)
void get_stack_name_from_id(const StackKey &key, std::string &name)
void set_timer_threshold(const timer_levels threshold)
TimerManager< NewTimer > & getGlobalTimerManager()
static const int max_level
void print_stack(Communicate *comm)
std::string get_timer_threshold_string() const