https://mooseframework.inl.gov
Loading...
Searching...
No Matches
PerfGraphLivePrint.C
Go to the documentation of this file.
1//* This file is part of the MOOSE framework
2//* https://mooseframework.inl.gov
3//*
4//* All rights reserved, see COPYRIGHT for full restrictions
5//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6//*
7//* Licensed under LGPL 2.1, please see LICENSE for details
8//* https://www.gnu.org/licenses/lgpl-2.1.html
9
10#include "PerfGraphLivePrint.h"
11#include "PerfGraphRegistry.h"
12#include "MooseApp.h"
13
16 _app(app),
17 _perf_graph(perf_graph),
18 _perf_graph_registry(moose::internal::getPerfGraphRegistry()),
19 _execution_list(perf_graph._execution_list),
20 _currently_destructing(false),
21 _time_limit(perf_graph._live_print_time_limit),
22 _mem_limit(perf_graph._live_print_mem_limit),
23 _stack_level(0),
24 _current_execution_list_end(0),
25 _current_execution_list_last(0),
26 _last_execution_list_end(0),
27 _last_printed_increment(NULL),
28 _last_num_printed(0),
29 _console_num_printed(0),
30 _stack_top_print_dots(true)
31{
32}
33
34void
36{
37 auto & section_info = _perf_graph_registry.sectionInfo(section_increment._id);
38
39 // If we're not printing dots - we shouldn't be printing the message at all
40 if (!section_info._print_dots || !_stack_top_print_dots)
41 {
43 _last_printed_increment = &section_increment;
44 return;
45 }
46
47 // If the live_message is empty - just print the name
48 auto message =
49 !section_info._live_message.empty() ? section_info._live_message : section_info._name;
50
51 // If this section is just started - but other stuff has printed before we got to print its
52 // message, we need to print it anyway because it could be lengthy and print unexplained
53 // dots to the console (until the Finished message). "Currently" conveys the message
54 // that we did not just start it, but we are doing that right now
56 section_increment._state == PerfGraph::IncrementState::STARTED &&
58 {
59 message = "Currently " + message;
60 // The 10 is for "Currently "
61 section_increment._num_dots = 10;
62 }
63
64 // This line is different - need to finish the last line
65 if (_last_printed_increment && _last_printed_increment != &section_increment &&
67 section_info._print_dots)
68 _console << '\n';
69
70 // Do we need to print dots?
71 if (_last_printed_increment && _last_printed_increment == &section_increment &&
72 section_increment._state == PerfGraph::IncrementState::PRINTED)
73 {
74 if (section_info._print_dots)
75 {
76 _console << ".";
77 section_increment._num_dots++;
78 }
79 }
80 // Printed before so print "Still"
81 else if (section_increment._state == PerfGraph::IncrementState::PRINTED)
82 {
83 _console << std::string(2 * section_increment._print_stack_level, ' ') << "Still " << message;
84
85 // If we're not printing dots - just finish the line
86 if (!section_info._print_dots)
87 _console << '\n';
88
89 // The 6 is for "Still "
90 section_increment._num_dots = 6;
91 }
92 else // Just print the message
93 {
94 _console << std::string(2 * section_increment._print_stack_level, ' ') << message;
95
96 // If we're not printing dots - just finish the line
97 if (!section_info._print_dots)
98 _console << '\n';
99
100 // Reset the dots since we're printing, except in the "Currently" case
101 if (section_increment._state != PerfGraph::IncrementState::STARTED ||
103 section_increment._beginning_num_printed == _console_num_printed)
104 section_increment._num_dots = 0;
105 }
106
107 section_increment._state = PerfGraph::IncrementState::PRINTED;
108
109 // Get the message to the screen
110 _console << std::flush;
111
112 // Keep track of where we printed in the console
114
115 _last_printed_increment = &section_increment;
116}
117
118void
120 PerfGraph::SectionIncrement & section_increment_finish)
121{
122 if (_stack_level < 1)
123 return;
124
125 mooseAssert(_perf_graph_registry.sectionExists(section_increment_start._id),
126 "Not found in map: " << section_increment_start._id);
127
128 auto & section_info_start = _perf_graph_registry.sectionInfo(section_increment_start._id);
129
130 // If the live_message is empty - just print the name
131 auto message = !section_info_start._live_message.empty() ? section_info_start._live_message
132 : section_info_start._name;
133
134 auto time_increment =
135 std::chrono::duration<double>(section_increment_finish._time - section_increment_start._time)
136 .count();
137
138 auto memory_total = section_increment_finish._memory;
139
140 auto num_horizontal_chars = message.size() + (2 * section_increment_start._print_stack_level);
141
142 // Add multiapps prefix size
143 if (!_app.isUltimateMaster())
144 num_horizontal_chars += _app.name().size() + 2;
145
146 // Do we need to print "Finished"?
147 // This happens after something else printed in-between when this increment started and finished
148 if (!section_info_start._print_dots ||
149 (_last_printed_increment && _last_printed_increment != &section_increment_start) ||
150 (section_increment_start._beginning_num_printed !=
151 _console_num_printed)) // This means someone _else_ printed
152 {
153 // If we had printed some dots - we need to finish the line
157 _console << '\n';
158
159 _console << std::string(2 * section_increment_start._print_stack_level, ' ') << "Finished "
160 << message;
161
162 // 9 is for "Finished "
163 num_horizontal_chars += 9;
164 }
165 else
166 num_horizontal_chars += section_increment_start._num_dots;
167
168 // Actually do the printing
169 _console << std::setw(WRAP_LENGTH - num_horizontal_chars);
170
171 _console << " [";
172 _console << COLOR_YELLOW;
173 _console << std::setw(6) << std::fixed << std::setprecision(2) << time_increment << " s";
174 _console << COLOR_DEFAULT;
175 _console << ']' << " [";
176 _console << COLOR_YELLOW << std::setw(5) << std::fixed;
177 _console << memory_total;
178 _console << " MB";
179 _console << COLOR_DEFAULT;
180 _console << ']';
181
182 // If we're not printing dots - just finish the line
183 _console << std::endl;
184
186
187 _last_printed_increment = &section_increment_finish;
188}
189
190void
192{
193 if (_stack_level < 1)
194 return;
195
196 // We need to print out everything on the stack before this that hasn't already been printed...
197 for (unsigned int s = 0; s < _stack_level - 1; s++)
198 {
199 auto & section = _print_thread_stack[s];
200
201 // Hasn't been printed at all and nothing else has been printed since this started
202 if (section._state == PerfGraph::IncrementState::STARTED)
203 printLiveMessage(section);
204
205 section._state = PerfGraph::IncrementState::PRINTED;
206 }
207}
208
209void
211{
212 // If someone else printed since, then we need to start over, and set everything on the stack to
213 // printed Everything is set to printed because if something printed and we're still in the same
214 // place then we need to NOT print out the beginning message
216 {
217 _last_printed_increment = nullptr;
218
219 for (unsigned int s = 0; s < _stack_level; s++)
221
222 return;
223 }
224
225 // Only print if there is something to print!
226 if (_stack_level > 0)
227 {
230
232
234
235 // Reset this each time
237 }
238}
239
240void
242{
243 // Current position in the execution list
245
246 while (p != _current_execution_list_end)
247 {
248 // The ternary here is for wrapping around
249 auto next_p = p + 1 < MAX_EXECUTION_LIST_SIZE ? p + 1 : 0;
250
251 auto & section_increment = _execution_list[p];
252
253 // New section, add to the stack
254 if (section_increment._state == PerfGraph::IncrementState::STARTED)
255 {
256 section_increment._print_stack_level = _stack_level;
257
258 // Store this increment in the stack
259 _print_thread_stack[_stack_level] = section_increment;
260
261 _stack_level++;
262 }
263 else // This means it's finished need to see if we need to print it
264 {
265 mooseAssert(_stack_level, "Popping beyond the beginning of the stack!");
266
267 // Get the beginning information for this section... it is the thing currently on the top of
268 // the stack
269 auto & section_increment_start = _print_thread_stack[_stack_level - 1];
270
271 auto time_increment =
272 std::chrono::duration<double>(section_increment._time - section_increment_start._time)
273 .count();
274 auto memory_increment = section_increment._memory - section_increment_start._memory;
275
276 // If it has already been printed or meets our criteria then print it and finish it
278 section_increment_start._state == PerfGraph::IncrementState::PRINTED ||
279 time_increment > _time_limit.load(std::memory_order_relaxed) ||
280 memory_increment > _mem_limit.load(std::memory_order_relaxed))
281 {
283
284 printStats(section_increment_start, section_increment);
285 }
286
287 _stack_level--;
288 }
289
290 p = next_p;
291 }
292}
293
294void
296{
297 // Keep going until we're signaled to end
298 // Note that _currently_destructing can only be set to true in this thread
299 // Which means that by the time we make it back to the top of the loop, either
300 // there was nothing to process or everything has been processed.
302 {
303 std::unique_lock<std::mutex> lock(_perf_graph._destructing_mutex);
304
305 // Wait for five seconds (by default), or until notified that a section is finished
306 // For a section to have finished the execution list has to have been appended to
307 // This keeps spurious wakeups from happening
308 // Note that the `lock` is only protecting _destructing since the execution list uses atomics.
309 // It must be atomic in order to keep the main thread from having to lock as it
310 // executes. The only downside to this is that it is possible for this thread to wake,
311 // check the condition, miss the notification, then wait. In our case this is not detrimental,
312 // as the only thing that will happen is we will wait 5 more seconds. This is also very
313 // unlikely.
314 // One other thing: wait_for() is not guaranteed to wait for 5 seconds. "Spurious" wakeups
315 // can occur - but the predicate here keeps us from doing anything in that case.
316 // This will either wait until 5 seconds have passed, the signal is sent, _or_ a spurious
317 // wakeup happens to find that there is work to do.
319 lock,
320 std::chrono::duration<Real>(_time_limit.load(std::memory_order_relaxed)),
321 [this]
322 {
323 // Get destructing first so that the execution_list will be in sync
324 this->_currently_destructing = _perf_graph._destructing;
325
326 // The end will be one past the last
327 // This "acquire" synchronizes with the "release" in the PerfGraph
328 // to ensure that all of the writes to the execution list have been
329 // published to this thread for the "end" we're reading
330 this->_current_execution_list_end =
331 _perf_graph._execution_list_end.load(std::memory_order_acquire);
332
333 // Save off the number of things currently printed to the console
334 this->_console_num_printed = _console.numPrinted();
335
336 // If we are destructing or there is new work to do... allow moving on
337 return this->_currently_destructing ||
338 this->_last_execution_list_end != this->_current_execution_list_end;
339 });
340
341 // If the PerfGraph is destructing and we don't have anything left to print - we need to quit
342 // Otherwise, if there are still things to print - do it... afterwards, the loop above
343 // will end because _done_future has been set in PerfGraph.
344 if (this->_currently_destructing &&
346 return;
347
348 // The last entry in the current execution list for convenience
349 _current_execution_list_last = static_cast<long int>(_current_execution_list_end) - 1 >= 0
351 : MAX_EXECUTION_LIST_SIZE;
352
353 // Only happens if nothing has been added
355 continue;
356
357 // Iterate from the last thing printed (begin) to the last thing in the list (end)
358 // If the time or memory of any section is above the threshold, print everything in between and
359 // update begin
360
361 // Are we still sitting in the same place as the last iteration? If so, we need to print
362 // progress and exit
364 inSamePlace();
365
366 // This means that new stuff has been added to the execution list. We need to iterate through
367 // it, modifying the stack and printing anything that needs printing
369
371
373 }
374}
An inteface for the _console for outputting to the Console object.
const ConsoleStream _console
An instance of helper class to write streams to the Console objects.
unsigned long long int numPrinted() const
The number of times something has been printed.
Base class for MOOSE-based applications.
Definition MooseApp.h:110
bool isUltimateMaster() const
Whether or not this app is the ultimate master app.
Definition MooseApp.h:866
const std::string & name() const
Get the name of the class.
Definition MooseBase.h:103
bool _stack_top_print_dots
Whether or not the top thing on the stack is set to print dots.
std::atomic< bool > _currently_destructing
True when we stop printing.
void inSamePlace()
What to do if we're still in the same spot.
void start()
Start printing.
const unsigned int WRAP_LENGTH
Number of columns before wrapping.
unsigned long long int _last_num_printed
The output count from the console the last time we printed.
std::array< PerfGraph::SectionIncrement, MOOSE_MAX_STACK_SIZE > _print_thread_stack
The current stack for what the print thread has seen.
void iterateThroughExecutionList()
What to do if there are new things in the execution list.
std::atomic< unsigned int > & _mem_limit
Limit (in MB)
void printLiveMessage(PerfGraph::SectionIncrement &section_increment)
Print the live message.
void printStackUpToLast()
Print everything underneath the current top of the stack.
unsigned int _current_execution_list_last
The actual last entry in the list This is useful because it is a circular queue - so this is not just...
unsigned long long int _console_num_printed
The current output count from the console.
PerfGraphLivePrint(PerfGraph &perf_graph, MooseApp &app)
unsigned int _last_execution_list_end
Where the end of the execution list was during the last call If this == the current_end....
const MooseApp & _app
The app performing this print.
void printStats(PerfGraph::SectionIncrement &section_increment_start, PerfGraph::SectionIncrement &section_increment_finish)
Print the stats.
std::atomic< Real > & _time_limit
Limit (in seconds) before printing.
PerfGraph::SectionIncrement * _last_printed_increment
Which increment was last printed.
unsigned int _current_execution_list_end
The end of the execution list This is (safely) copied from PerfGraph so that it is consistent for an ...
const moose::internal::PerfGraphRegistry & _perf_graph_registry
Reference to the PerfGraphRegistry for convenience.
unsigned int _stack_level
This is one beyond the last thing on the stack.
std::array< PerfGraph::SectionIncrement, MAX_EXECUTION_LIST_SIZE > & _execution_list
Convenience reference to the execution_list within the PerfGraph.
PerfGraph & _perf_graph
Reference to the PerfGraph to work with.
Use to hold an increment of time and memory for a section This is used in the LivePrint capability.
Definition PerfGraph.h:265
unsigned long long int _beginning_num_printed
The _console numPrinted() at the time this section was created.
Definition PerfGraph.h:296
unsigned int _print_stack_level
How much to indent this section.
Definition PerfGraph.h:284
long int _memory
Either the starting memory or final memory depending on _state.
Definition PerfGraph.h:293
std::chrono::time_point< std::chrono::steady_clock > _time
Either the starting time or final time depending on _state.
Definition PerfGraph.h:290
unsigned int _num_dots
How many dots have been printed for this section.
Definition PerfGraph.h:287
IncrementState _state
Whether or not this increment is the start of an increment or the finishing of an increment.
Definition PerfGraph.h:281
The PerfGraph will hold the master list of all registered performance segments and the head PerfNode.
Definition PerfGraph.h:44
std::condition_variable _finished_section
The condition_variable to wake the print thread.
Definition PerfGraph.h:406
std::mutex _destructing_mutex
The mutex to use with a condition_variable predicate to guard _destructing.
Definition PerfGraph.h:403
@ PRINTED
This section has already started printing.
Definition PerfGraph.h:254
@ STARTED
Section just started running.
Definition PerfGraph.h:251
bool _live_print_all
Whether or not to put everything in the perf graph.
Definition PerfGraph.h:338
const PerfGraphSectionInfo & sectionInfo(const PerfID section_id) const
Given a PerfID return the PerfGraphSectionInfo @section_id The ID.
bool sectionExists(const std::string &section_name) const
Whether or not a section with that name has been registered @section_name The name of the section.
std::string _live_message
Message to print while the section is running.
bool _print_dots
Whether or not to print dots while this section runs.