deal.II version GIT relicensing-6809-ge913b9bb34 2026-09-25 17:20:01+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
timer.cc
Go to the documentation of this file.
1// -----------------------------------------------------------------------------
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later
4// Copyright (C) 1998 - 2026 by the deal.II authors
5//
6// This file is part of the deal.II library.
7//
8// Detailed license information governing the source code and contributions
9// can be found in LICENSE.md and CONTRIBUTING.md at the top level directory.
10//
11// -----------------------------------------------------------------------------
12
14#include <deal.II/base/mpi.h>
15#include <deal.II/base/mpi.templates.h>
17#include <deal.II/base/timer.h>
19
20#include <boost/io/ios_state.hpp>
21
22#include <algorithm>
23#include <chrono>
24#include <iomanip>
25#include <iostream>
26#include <map>
27#include <mutex>
28#include <optional>
29#include <sstream>
30#include <string>
31#include <type_traits>
32
33#ifdef DEAL_II_HAVE_SYS_RESOURCE_H
34# include <sys/resource.h>
35#endif
36
37#ifdef DEAL_II_MSVC
38# include <windows.h>
39#endif
40
41
42
44
45namespace internal
46{
47 namespace TimerImplementation
48 {
49 namespace
50 {
55 template <typename T>
56 struct is_duration : std::false_type
57 {};
58
62 template <typename Rep, typename Period>
63 struct is_duration<std::chrono::duration<Rep, Period>> : std::true_type
64 {};
65
71 template <typename T>
72 T
73 from_seconds(const double time)
74 {
75 static_assert(is_duration<T>::value,
76 "The template type should be a duration type.");
77 return T(std::lround(T::period::den * (time / T::period::num)));
78 }
79
84 template <typename Rep, typename Period>
85 double
86 to_seconds(const std::chrono::duration<Rep, Period> duration)
87 {
88 return Period::num * double(duration.count()) / Period::den;
89 }
90
94 void
95 clear_timing_data(Utilities::MPI::MinMaxAvg &data)
96 {
97 data.sum = numbers::signaling_nan<double>();
98 data.min = numbers::signaling_nan<double>();
99 data.max = numbers::signaling_nan<double>();
100 data.avg = numbers::signaling_nan<double>();
103 }
104 } // namespace
105 } // namespace TimerImplementation
106} // namespace internal
107
108
109
112{
113 double system_cpu_duration = 0.0;
114#ifdef DEAL_II_MSVC
115 FILETIME cpuTime, sysTime, createTime, exitTime;
116 const auto succeeded = GetProcessTimes(
117 GetCurrentProcess(), &createTime, &exitTime, &sysTime, &cpuTime);
118 if (succeeded)
119 {
120 system_cpu_duration =
121 (double)(((unsigned long long)cpuTime.dwHighDateTime << 32) |
122 cpuTime.dwLowDateTime) /
123 1e7;
124 }
125 // keep the zero value if GetProcessTimes didn't work
126#elif defined(DEAL_II_HAVE_SYS_RESOURCE_H)
127 rusage usage;
128 getrusage(RUSAGE_SELF, &usage);
129 system_cpu_duration = usage.ru_utime.tv_sec + 1.e-6 * usage.ru_utime.tv_usec;
130#else
131 DEAL_II_WARNING("Unsupported platform. Porting not finished.")
132#endif
133 return time_point(
134 internal::TimerImplementation::from_seconds<duration>(system_cpu_duration));
135}
136
137
138
139template <typename clock_type_>
141 : current_lap_start_time(clock_type::now())
142 , accumulated_time(duration_type::zero())
143 , last_lap_time(duration_type::zero())
144{}
145
146
147
148template <typename clock_type_>
149void
151{
152 current_lap_start_time = clock_type::now();
153 accumulated_time = duration_type::zero();
154 last_lap_time = duration_type::zero();
155}
156
157
158
160 : Timer(MPI_COMM_SELF, /*sync_lap_times=*/false)
161{}
162
163
164
165Timer::Timer(const MPI_Comm mpi_communicator, const bool sync_lap_times_)
166 : running(false)
167 , is_synchronized(true)
168 , mpi_communicator(mpi_communicator)
169 , sync_lap_times(sync_lap_times_)
170{
171 reset();
172 start();
173}
174
175
176
177void
179{
180 // If the data of the last lap has not been synchronized so far,
181 // synchronize now, before starting to measure a new lap.
182 if (is_running() == false)
184
185 // Make sure only one thread at a time starts or resets the timer
186 // Note that the location of this line, after the call to
187 // synchronize_and_update(), but before the number of laps is increased
188 // is critical, since the same mutex is locked inside
189 // synchronize_and_update().
190 std::scoped_lock lock(mutex);
191
192 if (is_running() == false)
193 {
194 ++n_timed_laps;
195 running = true;
196 }
197
198#ifdef DEAL_II_WITH_MPI
199 if (sync_lap_times)
200 {
201 const int ierr = MPI_Barrier(mpi_communicator);
202 AssertThrowMPI(ierr);
203 }
204#endif
205 wall_times.current_lap_start_time = wall_clock_type::now();
206 cpu_times.current_lap_start_time = cpu_clock_type::now();
207}
208
209
210
211void
213{
214 // Make sure only one thread at a time stops the timer
215 std::scoped_lock lock(mutex);
216
217 AssertThrow(is_running() == true,
219 "This timer is not running and hence cannot be stopped."));
220
221 running = false;
222 is_synchronized = false;
223
225 wall_clock_type::now() - wall_times.current_lap_start_time;
226 cpu_times.last_lap_time =
227 cpu_clock_type::now() - cpu_times.current_lap_start_time;
228}
229
230
231
232bool
234{
235 return running;
236}
237
238
239
240double
242{
243 if (is_running())
244 {
245 const double running_time = internal::TimerImplementation::to_seconds(
246 cpu_clock_type::now() - cpu_times.current_lap_start_time +
247 cpu_times.accumulated_time);
248 return Utilities::MPI::sum(running_time, mpi_communicator);
249 }
250 else
251 {
253
254 return Utilities::MPI::sum(internal::TimerImplementation::to_seconds(
255 cpu_times.accumulated_time),
257 }
258}
259
260
261
262double
264{
265 if (running == false)
267
268 return internal::TimerImplementation::to_seconds(cpu_times.last_lap_time);
269}
270
271
272
273double
275{
276 wall_clock_type::duration current_elapsed_wall_time;
277 if (is_running())
278 {
279 current_elapsed_wall_time = wall_clock_type::now() -
282 }
283 else
284 {
286
287 current_elapsed_wall_time = wall_times.accumulated_time;
288 }
289
290 return internal::TimerImplementation::to_seconds(current_elapsed_wall_time);
291}
292
293
294
295double
297{
298 if (running == false)
300
301 return internal::TimerImplementation::to_seconds(wall_times.last_lap_time);
302}
303
304
305
306void
308{
309 Assert(
310 running == false,
312 "Timer::synchronize_and_update() should not be called while the timer is running."));
313
314 // Make sure only one thread synchronizes the shared state
315 // and all other threads wait until synchronization is done.
316 std::scoped_lock lock(mutex);
317
318 if (is_synchronized == false)
319 {
321 Utilities::MPI::min_max_avg(internal::TimerImplementation::to_seconds(
324
325 const Utilities::MPI::MinMaxAvg last_lap_cpu_time_data =
326 Utilities::MPI::min_max_avg(internal::TimerImplementation::to_seconds(
327 cpu_times.last_lap_time),
329
330 if (sync_lap_times)
331 {
333 internal::TimerImplementation::from_seconds<
334 decltype(wall_times)::duration_type>(last_lap_wall_time_data.max);
336 cpu_times.last_lap_time = internal::TimerImplementation::from_seconds<
337 decltype(cpu_times)::duration_type>(last_lap_cpu_time_data.max);
338 }
339
340 // This looks like we should set last_lap_time to 0 after
341 // updating the accumulated time but both numbers may
342 // be requested after the update. We therefore instead ensure
343 // that synchronize_and_update() is only executed once after
344 // each lap, by setting is_synchronized to true below.
346 cpu_times.accumulated_time += cpu_times.last_lap_time;
347
349 Utilities::MPI::min_max_avg(internal::TimerImplementation::to_seconds(
352
353 is_synchronized = true;
354 }
355}
356
357
358
359void
361{
363 cpu_times.reset();
364 running = false;
365 n_timed_laps = 0;
366 is_synchronized = true;
367 internal::TimerImplementation::clear_timing_data(last_lap_wall_time_data);
368 internal::TimerImplementation::clear_timing_data(accumulated_wall_time_data);
369}
370
371
372
373unsigned int
375{
376 return n_timed_laps;
377}
378
379
380
381/* ---------------------------- TimerOutput -------------------------- */
382
383TimerOutput::TimerOutput(std::ostream &stream,
384 const OutputFrequency output_frequency,
385 const OutputType output_type)
386 : output_frequency(output_frequency)
387 , output_type(output_type)
388 , timer_all()
389 , sections()
390 , out_stream(stream, true)
391 , output_is_enabled(true)
392 , mpi_communicator_timing(std::nullopt)
393{}
394
395
396
398 const OutputFrequency output_frequency,
399 const OutputType output_type)
400 : output_frequency(output_frequency)
401 , output_type(output_type)
402 , timer_all()
403 , sections()
404 , out_stream(stream)
405 , output_is_enabled(true)
406 , mpi_communicator_timing(std::nullopt)
407{}
408
409
410
411TimerOutput::TimerOutput(const MPI_Comm mpi_communicator,
412 std::ostream &stream,
413 const OutputFrequency output_frequency,
414 const OutputType output_type)
415 : output_frequency(output_frequency)
416 , output_type(output_type)
417 , timer_all(mpi_communicator, /* sync_lap_times */ false)
418 , sections()
419 , out_stream(stream, true)
420 , output_is_enabled(true)
421 , mpi_communicator_timing(mpi_communicator)
422{}
423
424
425
426TimerOutput::TimerOutput(const MPI_Comm mpi_communicator,
427 ConditionalOStream &stream,
428 const OutputFrequency output_frequency,
429 const OutputType output_type)
430 : output_frequency(output_frequency)
431 , output_type(output_type)
432 , timer_all(mpi_communicator, /* sync_lap_times */ false)
433 , sections()
434 , out_stream(stream)
435 , output_is_enabled(true)
436 , mpi_communicator_timing(mpi_communicator)
437{}
438
439
440
442{
443 auto do_exit = [this]() {
444 try
445 {
446 while (active_sections.size() > 0)
448 // don't print unless we leave all subsections
449 if ((output_frequency == summary ||
451 output_is_enabled == true)
453 }
454 catch (...)
455 {}
456 };
457
458 // avoid communicating with other processes if there is an uncaught
459 // exception
460#ifdef DEAL_II_WITH_MPI
461 if (std::uncaught_exceptions() > 0 && mpi_communicator_timing.has_value() &&
463 {
464 const unsigned int myid =
466 if (myid == 0)
467 std::cerr
468 << "---------------------------------------------------------\n"
469 << "TimerOutput objects finalize timed values printed to the\n"
470 << "screen by communicating over MPI in their destructors.\n"
471 << "Since an exception is currently uncaught, this\n"
472 << "synchronization (and subsequent output) will be skipped\n"
473 << "to avoid a possible deadlock.\n"
474 << "---------------------------------------------------------"
475 << std::endl;
476 }
477 else
478 {
479 do_exit();
480 }
481#else
482 do_exit();
483#endif
484}
485
486
487
488void
489TimerOutput::enter_subsection(const std::string &section_name)
490{
491 std::scoped_lock lock(mutex);
492
493 Assert(section_name.empty() == false, ExcMessage("Section string is empty."));
494
495 Assert(std::find(active_sections.begin(),
496 active_sections.end(),
497 section_name) == active_sections.end(),
498 ExcMessage("Cannot enter the already active section <" + section_name +
499 ">."));
500
501 if (sections.find(section_name) == sections.end())
502 {
503 // Ensure MPI operations only happen if an MPI communicator was
504 // initialized. No need to call start() for the timers, since
505 // the constructor already starts them.
506 if (mpi_communicator_timing.has_value())
507 sections[section_name] = Timer(*mpi_communicator_timing, true);
508 else
509 sections[section_name] = Timer(MPI_COMM_SELF, false);
510 }
511 else
512 {
513 Assert(sections[section_name].is_running() == false,
514 ExcMessage("Cannot enter the already active section <" +
515 section_name + ">."));
516
517 sections[section_name].start();
518 }
519
520 active_sections.push_back(section_name);
521}
522
523
524
525void
526TimerOutput::leave_subsection(const std::string &section_name)
527{
528 Assert(!active_sections.empty(),
529 ExcMessage("Cannot exit any section because none has been entered!"));
530
531 std::scoped_lock lock(mutex);
532
533 if (!section_name.empty())
534 {
535 Assert(sections.find(section_name) != sections.end(),
536 ExcMessage("Cannot delete a section that was never created."));
537 Assert(std::find(active_sections.begin(),
538 active_sections.end(),
539 section_name) != active_sections.end(),
540 ExcMessage("Cannot delete a section that has not been entered."));
541 }
542
543 // if no string is given, exit the last
544 // active section.
545 const std::string actual_section_name =
546 (section_name.empty() ? active_sections.back() : section_name);
547
548 Assert(sections[actual_section_name].is_running() == true,
549 ExcMessage("Cannot leave section <" + actual_section_name +
550 ">, because it has not been entered."));
551
552 sections[actual_section_name].stop();
553
554 // In case we have to print out something, do that here,
555 // but only if no exceptions are currently uncaught.
556 // If there are uncaught exceptions we are currently in the
557 // process of stack unwinding, and the code below would trigger
558 // MPI communication, which leads to a deadlock if not all
559 // processes threw the exception. Avoid this deadlock by not
560 // writing output in this case.
563 output_is_enabled == true && std::uncaught_exceptions() == 0)
564 {
565 std::string output_time;
566 std::ostringstream cpu;
567 cpu << sections[actual_section_name].last_cpu_time() << "s";
568 std::ostringstream wall;
569 wall << sections[actual_section_name].last_wall_time() << "s";
570 if (output_type == cpu_times)
571 output_time = ", CPU time: " + cpu.str();
572 else if (output_type == wall_times)
573 output_time = ", wall time: " + wall.str() + ".";
574 else
575 output_time =
576 ", CPU/wall time: " + cpu.str() + " / " + wall.str() + ".";
577
578 out_stream << actual_section_name << output_time << std::endl;
579 }
580
581 // delete the index from the list of
582 // active ones
583 active_sections.erase(std::find(active_sections.begin(),
584 active_sections.end(),
585 actual_section_name));
586}
587
588
589
590std::map<std::string, double>
592{
593 Assert(
594 active_sections.empty(),
596 "Cannot access data from TimerOutput while inside a timed section."));
597
598 std::map<std::string, double> output;
599 for (const auto &section : sections)
600 {
601 switch (kind)
602 {
604 output[section.first] = section.second.cpu_time();
605 break;
607 output[section.first] = section.second.wall_time();
608 break;
610 output[section.first] = section.second.n_laps();
611 break;
612 default:
614 }
615 }
616 return output;
617}
618
619
620
621void
623{
624 Assert(active_sections.empty(),
626 "Cannot print data from TimerOutput while inside a timed section."));
627
628 // we are going to change the precision and width of output below. store the
629 // old values so the get restored when exiting this function
630 const boost::io::ios_base_all_saver restore_stream(out_stream.get_stream());
631
632 // get the maximum width among all sections
633 unsigned int max_width = 0;
634 for (const auto &i : sections)
635 max_width = std::max(max_width, static_cast<unsigned int>(i.first.size()));
636
637 // 32 is the default width until | character
638 max_width = std::max(max_width + 1, static_cast<unsigned int>(32));
639 const std::string extra_dash = std::string(max_width - 32, '-');
640 const std::string extra_space = std::string(max_width - 32, ' ');
641
643 {
644 // in case we want to write CPU times
645 if (output_type != wall_times)
646 {
648
649 // check that the sum of all section times is less or equal than the
650 // total time. otherwise, we might have generated a lot of overhead in
651 // this function.
652 double total_cpu_time_in_sections = 0.;
653 for (const auto &i : sections)
654 total_cpu_time_in_sections += i.second.cpu_time();
655
656 const double section_overhead =
657 total_cpu_time_in_sections - total_cpu_time;
658 if (section_overhead > 0.0)
659 total_cpu_time = total_cpu_time_in_sections;
660
661 // generate a nice table
662 out_stream << "\n\n"
663 << "+---------------------------------------------"
664 << extra_dash << "+------------"
665 << "+------------+\n"
666 << "| Total CPU time elapsed since start "
667 << extra_space << "|";
668 out_stream << std::setw(10) << std::setprecision(3) << std::right;
669 out_stream << total_cpu_time << "s | |\n";
670 out_stream << "| "
671 << extra_space << "| "
672 << "| |\n";
673 out_stream << "| Section " << extra_space
674 << "| no. calls |";
675 out_stream << std::setw(10);
676 out_stream << std::setprecision(3);
677 out_stream << " CPU time "
678 << " | % of total |\n";
679 out_stream << "+---------------------------------" << extra_dash
680 << "+-----------+------------"
681 << "+------------+";
682 for (const auto &i : sections)
683 {
684 std::string name_out = i.first;
685
686 // resize the array so that it is always of the same size
687 unsigned int pos_non_space = name_out.find_first_not_of(' ');
688 name_out.erase(0, pos_non_space);
689 name_out.resize(max_width, ' ');
690
691 const double section_cpu_time = i.second.cpu_time();
692
693 out_stream << std::endl;
694 out_stream << "| " << name_out;
695 out_stream << "| ";
696 out_stream << std::setw(9);
697 out_stream << i.second.n_laps() << " |";
698 out_stream << std::setw(10);
699 out_stream << std::setprecision(3);
700 out_stream << section_cpu_time << "s |";
701 out_stream << std::setw(10);
702 if (total_cpu_time != 0)
703 {
704 // if run time was less than 0.1%, just print a zero to avoid
705 // printing silly things such as "2.45e-6%". otherwise print
706 // the actual percentage
707 const double fraction = section_cpu_time / total_cpu_time;
708 if (fraction > 0.001)
709 {
710 out_stream << std::setprecision(2);
711 out_stream << fraction * 100;
712 }
713 else
714 out_stream << 0.0;
715
716 out_stream << "% |";
717 }
718 else
719 out_stream << 0.0 << "% |";
720 }
721 out_stream << std::endl
722 << "+---------------------------------" << extra_dash
723 << "+-----------+"
724 << "------------+------------+\n"
725 << std::endl;
726
727 if (section_overhead > 0.0)
729 << std::endl
730 << "Note: The sum of section times is " << section_overhead
731 << " seconds larger than the total time.\n"
732 << "(Timer function may have introduced too much overhead, or different\n"
733 << "section timers may have run at the same time.)" << std::endl;
734 }
735
736 // in case we want to write out wallclock times
737 if (output_type != cpu_times)
738 {
739 const double total_wall_time = timer_all.wall_time();
740
741 // now generate a nice table
742 out_stream << "\n\n"
743 << "+---------------------------------------------"
744 << extra_dash << "+------------"
745 << "+------------+\n"
746 << "| Total wallclock time elapsed since start "
747 << extra_space << "|";
748 out_stream << std::setw(10) << std::setprecision(3) << std::right;
749 out_stream << total_wall_time << "s | |\n";
750 out_stream << "| "
751 << extra_space << "| "
752 << "| |\n";
753 out_stream << "| Section " << extra_space
754 << "| no. calls |";
755 out_stream << std::setw(10);
756 out_stream << std::setprecision(3);
757 out_stream << " wall time | % of total |\n";
758 out_stream << "+---------------------------------" << extra_dash
759 << "+-----------+------------"
760 << "+------------+";
761 for (const auto &i : sections)
762 {
763 std::string name_out = i.first;
764
765 // resize the array so that it is always of the same size
766 unsigned int pos_non_space = name_out.find_first_not_of(' ');
767 name_out.erase(0, pos_non_space);
768 name_out.resize(max_width, ' ');
769 out_stream << std::endl;
770 out_stream << "| " << name_out;
771 out_stream << "| ";
772 out_stream << std::setw(9);
773 out_stream << i.second.n_laps() << " |";
774 out_stream << std::setw(10);
775 out_stream << std::setprecision(3);
776 out_stream << i.second.wall_time() << "s |";
777 out_stream << std::setw(10);
778
779 if (total_wall_time != 0)
780 {
781 // if run time was less than 0.1%, just print a zero to avoid
782 // printing silly things such as "2.45e-6%". otherwise print
783 // the actual percentage
784 const double fraction =
785 i.second.wall_time() / total_wall_time;
786 if (fraction > 0.001)
787 {
788 out_stream << std::setprecision(2);
789 out_stream << fraction * 100;
790 }
791 else
792 out_stream << 0.0;
793
794 out_stream << "% |";
795 }
796 else
797 out_stream << 0.0 << "% |";
798 }
799 out_stream << std::endl
800 << "+---------------------------------" << extra_dash
801 << "+-----------+"
802 << "------------+------------+\n"
803 << std::endl;
804 }
805 }
806 else
807 // output_type == cpu_and_wall_times_grouped
808 {
809 const double total_wall_time = timer_all.wall_time();
811
812 // check that the sum of all times is less or equal than the total time.
813 // otherwise, we might have generated a lot of overhead in this function.
814 double total_cpu_time_in_sections = 0.;
815 for (const auto &i : sections)
816 total_cpu_time_in_sections += i.second.cpu_time();
817
818 const double section_overhead =
819 total_cpu_time_in_sections - total_cpu_time;
820 if (section_overhead > 0.0)
821 total_cpu_time = total_cpu_time_in_sections;
822
823 // generate a nice table
824 out_stream << "\n\n+---------------------------------------------"
825 << extra_dash << "+"
826 << "------------+------------+"
827 << "------------+------------+" << '\n'
828 << "| Total CPU/wall time elapsed since start "
829 << extra_space << "|" << std::setw(10) << std::setprecision(3)
830 << std::right << total_cpu_time << "s | "
831 << extra_space << "|" << std::setw(10) << std::setprecision(3)
832 << total_wall_time << "s | |"
833 << "\n| "
834 << extra_space << "|"
835 << " | |"
836 << " | |"
837 << "\n| Section " << extra_space
838 << "| no. calls |"
839 << " CPU time | % of total |"
840 << " wall time | % of total |"
841 << "\n+---------------------------------" << extra_dash
842 << "+-----------+"
843 << "------------+------------+"
844 << "------------+------------+" << std::endl;
845
846 for (const auto &i : sections)
847 {
848 std::string name_out = i.first;
849
850 // resize the array so that it is always of the same size
851 unsigned int pos_non_space = name_out.find_first_not_of(' ');
852 name_out.erase(0, pos_non_space);
853 name_out.resize(max_width, ' ');
854 out_stream << "| " << name_out << "| ";
855
856 out_stream << std::setw(9);
857 out_stream << i.second.n_laps() << " |";
858
859 if (output_type != wall_times)
860 {
861 const double section_cpu_time = i.second.cpu_time();
862
863 out_stream << std::setw(10);
864 out_stream << std::setprecision(3);
865 out_stream << section_cpu_time << "s |";
866 out_stream << std::setw(10);
867 if (total_cpu_time != 0)
868 {
869 // if run time was less than 0.1%, just print a zero to avoid
870 // printing silly things such as "2.45e-6%". otherwise print
871 // the actual percentage
872 const double fraction = section_cpu_time / total_cpu_time;
873 if (fraction > 0.001)
874 {
875 out_stream << std::setprecision(2);
876 out_stream << fraction * 100;
877 }
878 else
879 out_stream << 0.0;
880
881 out_stream << "% |";
882 }
883 else
884 out_stream << 0.0 << "% |";
885 }
886
887 if (output_type != cpu_times)
888 {
889 out_stream << std::setw(10);
890 out_stream << std::setprecision(3);
891 out_stream << i.second.wall_time() << "s |";
892 out_stream << std::setw(10);
893
894 if (total_wall_time != 0)
895 {
896 // if run time was less than 0.1%, just print a zero to avoid
897 // printing silly things such as "2.45e-6%". otherwise print
898 // the actual percentage
899 const double fraction =
900 i.second.wall_time() / total_wall_time;
901 if (fraction > 0.001)
902 {
903 out_stream << std::setprecision(2);
904 out_stream << fraction * 100;
905 }
906 else
907 out_stream << 0.0;
908
909 out_stream << "% |";
910 }
911 else
912 out_stream << 0.0 << "% |";
913 }
914 out_stream << std::endl;
915 }
916
917 out_stream << "+---------------------------------" << extra_dash
918 << "+-----------+"
919 << "------------+------------+"
920 << "------------+------------+" << std::endl
921 << std::endl;
922
923 if (output_type != wall_times && section_overhead > 0.0)
925 << std::endl
926 << "Note: The sum of section times is " << section_overhead
927 << " seconds larger than the total time.\n"
928 << "(Timer function may have introduced too much overhead, or different\n"
929 << "section timers may have run at the same time.)" << std::endl;
930 }
931}
932
933
934
935void
937 const MPI_Comm mpi_communicator_statistics,
938 const double quantile) const
939{
940 Assert(active_sections.empty(),
942 "Cannot print data from TimerOutput while inside a timed section."));
943
944 Assert(quantile >= 0. && quantile <= 0.5,
945 ExcMessage("The quantile must be between 0 and 0.5"));
946
947#ifdef DEAL_II_WITH_MPI
948 // calling wall_time() below requires global communication over
949 // mpi_communicator_timing, if timers are constructed with an MPI
950 // communicator
951 // -> make sure the two communicators mpi_communicator_timing and
952 // mpi_communicator_statistics contain the same ranks
953 if (mpi_communicator_timing.has_value())
954 {
955 int result;
956 const int ierr = MPI_Comm_compare(mpi_communicator_statistics,
958 &result);
959 AssertThrowMPI(ierr);
960
961 Assert(result == MPI_IDENT || result == MPI_CONGRUENT ||
962 result == MPI_SIMILAR,
964 "The passed MPI communicator must contain the same processes as "
965 "the one used to construct the TimerOutput."));
966 }
967#endif
968
969 // we are going to change the precision and width of output below. store the
970 // old values so the get restored when exiting this function
971 const boost::io::ios_base_all_saver restore_stream(out_stream.get_stream());
972
973 // Obtain the global list of all sections entered by the ranks within
974 // mpi_communicator_statistics
975 std::vector<std::string> my_section_names;
976 my_section_names.reserve(sections.size());
977 for (const auto &[section, timer] : sections)
978 my_section_names.push_back(section);
979
980 const std::vector<std::string> global_section_names =
981 Utilities::MPI::compute_set_union(my_section_names,
982 mpi_communicator_statistics);
983
984 // get the maximum width among all sections
985 unsigned int max_width = 0;
986 for (const auto &i : global_section_names)
987 max_width = std::max(max_width, static_cast<unsigned int>(i.size()));
988
989 // 17 is the default width until | character
990 max_width = std::max(max_width + 1, static_cast<unsigned int>(17));
991 const std::string extra_dash = std::string(max_width - 17, '-');
992 const std::string extra_space = std::string(max_width - 17, ' ');
993
994 // function to print data in a nice table
995 const auto print_statistics = [&](const double given_time) {
996 const unsigned int n_ranks =
997 Utilities::MPI::n_mpi_processes(mpi_communicator_statistics);
998 if (n_ranks == 1 || quantile == 0.)
999 {
1001 Utilities::MPI::min_max_avg(given_time, mpi_communicator_statistics);
1002
1003 out_stream << std::setw(10) << std::setprecision(4) << std::right;
1004 out_stream << data.min << "s ";
1005 out_stream << std::setw(5) << std::right;
1006 out_stream << data.min_index << (n_ranks > 99999 ? "" : " ") << "|";
1007 out_stream << std::setw(10) << std::setprecision(4) << std::right;
1008 out_stream << data.avg << "s |";
1009 out_stream << std::setw(10) << std::setprecision(4) << std::right;
1010 out_stream << data.max << "s ";
1011 out_stream << std::setw(5) << std::right;
1012 out_stream << data.max_index << (n_ranks > 99999 ? "" : " ") << "|\n";
1013 }
1014 else
1015 {
1016 const unsigned int my_rank =
1017 Utilities::MPI::this_mpi_process(mpi_communicator_statistics);
1018 std::vector<double> receive_data(my_rank == 0 ? n_ranks : 0);
1019 std::vector<double> result(9);
1020#ifdef DEAL_II_WITH_MPI
1021 int ierr = MPI_Gather(&given_time,
1022 1,
1023 MPI_DOUBLE,
1024 receive_data.data(),
1025 1,
1026 MPI_DOUBLE,
1027 0,
1028 mpi_communicator_statistics);
1029 AssertThrowMPI(ierr);
1030 if (my_rank == 0)
1031 {
1032 // fill the received data in a pair and sort; on the way, also
1033 // compute the average
1034 std::vector<std::pair<double, unsigned int>> data_rank;
1035 data_rank.reserve(n_ranks);
1036 for (unsigned int i = 0; i < n_ranks; ++i)
1037 {
1038 data_rank.emplace_back(receive_data[i], i);
1039 result[4] += receive_data[i];
1040 }
1041 result[4] /= n_ranks;
1042 std::sort(data_rank.begin(), data_rank.end());
1043
1044 const unsigned int quantile_index =
1045 static_cast<unsigned int>(std::round(quantile * n_ranks));
1046 AssertIndexRange(quantile_index, data_rank.size());
1047 result[0] = data_rank[0].first;
1048 result[1] = data_rank[0].second;
1049 result[2] = data_rank[quantile_index].first;
1050 result[3] = data_rank[quantile_index].second;
1051 result[5] = data_rank[n_ranks - 1 - quantile_index].first;
1052 result[6] = data_rank[n_ranks - 1 - quantile_index].second;
1053 result[7] = data_rank[n_ranks - 1].first;
1054 result[8] = data_rank[n_ranks - 1].second;
1055 }
1056 ierr = MPI_Bcast(
1057 result.data(), 9, MPI_DOUBLE, 0, mpi_communicator_statistics);
1058 AssertThrowMPI(ierr);
1059#endif
1060 out_stream << std::setw(10) << std::setprecision(4) << std::right;
1061 out_stream << result[0] << "s ";
1062 out_stream << std::setw(5) << std::right;
1063 out_stream << static_cast<unsigned int>(result[1])
1064 << (n_ranks > 99999 ? "" : " ") << "|";
1065 out_stream << std::setw(10) << std::setprecision(4) << std::right;
1066 out_stream << result[2] << "s ";
1067 out_stream << std::setw(5) << std::right;
1068 out_stream << static_cast<unsigned int>(result[3])
1069 << (n_ranks > 99999 ? "" : " ") << "|";
1070 out_stream << std::setw(10) << std::setprecision(4) << std::right;
1071 out_stream << result[4] << "s |";
1072 out_stream << std::setw(10) << std::setprecision(4) << std::right;
1073 out_stream << result[5] << "s ";
1074 out_stream << std::setw(5) << std::right;
1075 out_stream << static_cast<unsigned int>(result[6])
1076 << (n_ranks > 99999 ? "" : " ") << "|";
1077 out_stream << std::setw(10) << std::setprecision(4) << std::right;
1078 out_stream << result[7] << "s ";
1079 out_stream << std::setw(5) << std::right;
1080 out_stream << static_cast<unsigned int>(result[8])
1081 << (n_ranks > 99999 ? "" : " ") << "|\n";
1082 }
1083 };
1084
1085 // in case we want to write out wallclock times
1086 {
1087 const unsigned int n_ranks =
1088 Utilities::MPI::n_mpi_processes(mpi_communicator_statistics);
1089
1090 const std::string time_rank_column = "------------------+";
1091 const std::string time_rank_space = " |";
1092
1093 // now generate a nice table
1094 out_stream << '\n'
1095 << "+------------------------------" << extra_dash << "+"
1096 << time_rank_column
1097 << (n_ranks > 1 && quantile > 0. ? time_rank_column : "")
1098 << "------------+"
1099 << (n_ranks > 1 && quantile > 0. ? time_rank_column : "")
1100 << time_rank_column << '\n'
1101 << "| Total wallclock time elapsed " << extra_space << "|";
1102
1103 print_statistics(timer_all.wall_time());
1104
1105 out_stream << "| " << extra_space << "|"
1106 << time_rank_space
1107 << (n_ranks > 1 && quantile > 0. ? time_rank_space : "")
1108 << " "
1109 << (n_ranks > 1 && quantile > 0. ? time_rank_space : "")
1110 << time_rank_space << '\n';
1111 out_stream << "| Section " << extra_space << "| no. calls "
1112 << "| min time rank |";
1113 if (n_ranks > 1 && quantile > 0.)
1114 out_stream << " " << std::setw(5) << std::setprecision(2) << std::right
1115 << quantile << "-tile rank |";
1116 out_stream << " avg time |";
1117 if (n_ranks > 1 && quantile > 0.)
1118 out_stream << " " << std::setw(5) << std::setprecision(2) << std::right
1119 << 1. - quantile << "-tile rank |";
1120 out_stream << " max time rank |\n";
1121 out_stream << "+------------------------------" << extra_dash << "+"
1122 << time_rank_column
1123 << (n_ranks > 1 && quantile > 0. ? time_rank_column : "")
1124 << "------------+"
1125 << (n_ranks > 1 && quantile > 0. ? time_rank_column : "")
1126 << time_rank_column << '\n';
1127
1128 for (const auto &global_section_name : global_section_names)
1129 {
1130 std::string name_out = global_section_name;
1131 const auto section_it = sections.find(global_section_name);
1132 const bool entered_section_locally = (section_it != sections.end());
1133
1134 // resize the array so that it is always of the same size
1135 unsigned int pos_non_space = name_out.find_first_not_of(' ');
1136 name_out.erase(0, pos_non_space);
1137 name_out.resize(max_width, ' ');
1138 out_stream << "| " << name_out;
1139 out_stream << "| ";
1140 out_stream << std::setw(9);
1141 const unsigned n_laps =
1142 entered_section_locally ? section_it->second.n_laps() : 0;
1143
1144 out_stream << n_laps << " |";
1145
1146 const double wall_time =
1147 entered_section_locally ? section_it->second.wall_time() : 0.0;
1148 print_statistics(wall_time);
1149 }
1150 out_stream << "+------------------------------" << extra_dash << "+"
1151 << time_rank_column
1152 << (n_ranks > 1 && quantile > 0. ? time_rank_column : "")
1153 << "------------+"
1154 << (n_ranks > 1 && quantile > 0. ? time_rank_column : "")
1155 << time_rank_column << '\n';
1156 }
1157}
1158
1159
1160
1161void
1167
1168
1169
1170void
1176
1177
1178
1179void
1181{
1182 std::scoped_lock lock(mutex);
1183 sections.clear();
1184 active_sections.clear();
1186}
1187
1188
1189
1191{
1192 try
1193 {
1194 stop();
1195 }
1196 catch (...)
1197 {}
1198}
1199
1200
std::ostream & get_stream() const
void set_condition(const bool active)
void reset()
Definition timer.cc:1180
void print_summary() const
Definition timer.cc:622
@ cpu_and_wall_times_grouped
Definition timer.h:761
@ cpu_times
Definition timer.h:749
@ wall_times
Definition timer.h:753
std::optional< MPI_Comm > mpi_communicator_timing
Definition timer.h:938
OutputFrequency output_frequency
Definition timer.h:896
void disable_output()
Definition timer.cc:1162
std::map< std::string, Timer > sections
Definition timer.h:913
void enable_output()
Definition timer.cc:1171
void leave_subsection(const std::string &section_name="")
Definition timer.cc:526
OutputType output_type
Definition timer.h:901
void print_wall_time_statistics(const MPI_Comm mpi_communicator_statistics, const double print_quantile=0.) const
Definition timer.cc:936
OutputFrequency
Definition timer.h:701
@ every_call
Definition timer.h:705
@ every_call_and_summary
Definition timer.h:713
std::list< std::string > active_sections
Definition timer.h:932
Threads::Mutex mutex
Definition timer.h:944
ConditionalOStream out_stream
Definition timer.h:918
@ total_wall_time
Definition timer.h:733
@ total_cpu_time
Definition timer.h:729
std::map< std::string, double > get_summary_data(const OutputData kind) const
Definition timer.cc:591
bool output_is_enabled
Definition timer.h:924
~TimerOutput()
Definition timer.cc:441
Timer timer_all
Definition timer.h:908
void enter_subsection(const std::string &section_name)
Definition timer.cc:489
TimerOutput(std::ostream &stream, const OutputFrequency output_frequency, const OutputType output_type)
Definition timer.cc:383
Definition timer.h:128
double last_cpu_time() const
Definition timer.cc:263
bool sync_lap_times
Definition timer.h:409
Threads::Mutex mutex
Definition timer.h:445
void synchronize_and_update() const
Definition timer.cc:307
void start()
Definition timer.cc:178
bool running
Definition timer.h:370
Utilities::MPI::MinMaxAvg accumulated_wall_time_data
Definition timer.h:427
unsigned int n_laps() const
Definition timer.cc:374
double cpu_time() const
Definition timer.cc:241
unsigned int n_timed_laps
Definition timer.h:434
Timer()
Definition timer.cc:159
void stop()
Definition timer.cc:212
double wall_time() const
Definition timer.cc:274
MPI_Comm mpi_communicator
Definition timer.h:403
ClockMeasurements< wall_clock_type > wall_times
Definition timer.h:359
ClockMeasurements< cpu_clock_type > cpu_times
Definition timer.h:365
void reset()
Definition timer.cc:360
bool is_running() const
Definition timer.cc:233
Utilities::MPI::MinMaxAvg last_lap_wall_time_data
Definition timer.h:417
void restart()
Definition timer.h:953
bool is_synchronized
Definition timer.h:396
double last_wall_time() const
Definition timer.cc:296
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
#define DEAL_II_WARNING(desc)
Definition config.h:712
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
#define DEAL_II_NOT_IMPLEMENTED()
#define Assert(cond, exc)
#define AssertThrowMPI(error_code)
#define AssertIndexRange(index, range)
static ::ExceptionBase & ExcMessage(std::string arg1)
#define AssertThrow(cond, exc)
const unsigned int my_rank
Definition mpi.cc:917
std::vector< index_type > data
Definition mpi.cc:734
T sum(const T &t, const MPI_Comm mpi_communicator)
unsigned int n_mpi_processes(const MPI_Comm mpi_communicator)
Definition mpi.cc:103
std::vector< T > compute_set_union(const std::vector< T > &vec, const MPI_Comm comm)
unsigned int this_mpi_process(const MPI_Comm mpi_communicator)
Definition mpi.cc:118
MinMaxAvg min_max_avg(const double my_value, const MPI_Comm mpi_communicator)
Definition mpi.cc:77
constexpr unsigned int invalid_unsigned_int
Definition types.h:228
STL namespace.
::VectorizedArray< Number, width > max(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
static time_point now() noexcept
Definition timer.cc:111
std::chrono::time_point< CPUClock, duration > time_point
Definition timer.h:57
clock_type_ clock_type
Definition timer.h:303
time_point_type current_lap_start_time
Definition timer.h:319
typename clock_type::duration duration_type
Definition timer.h:313
duration_type accumulated_time
Definition timer.h:324
duration_type last_lap_time
Definition timer.h:329