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
Classes | Public Types | Public Member Functions | Private Attributes | List of all members
TimerOutput Class Reference

#include <deal.II/base/timer.h>

Detailed Description

This class can be used to generate formatted output from time measurements of different subsections in a program. It is possible to create several sections that perform certain aspects of the program. A section can be entered several times. By changing the options in OutputFrequency and OutputType, the user can choose whether output should be generated every time a section is joined or just at the end of the program. Moreover, it is possible to show CPU times, wall times, or both.

To ensure output from this class is properly synchronized in parallel programs and to simplify internal data handling, output from this class can only be generated, if no subsection is currently being timed. I.e. all subsections have to be closed before producing output or accessing data.

The class is used in a substantial number of tutorial programs that collect timing data. step-77 is an example of a relatively simple sequential program that uses it. step-40 and several others mentioned below use it for parallel computations.

Usage

Use of this class could be as follows:

timer.enter_subsection ("Setup dof system");
setup_dofs();
timer.leave_subsection();
timer.enter_subsection ("Assemble");
assemble_system_1();
timer.leave_subsection();
timer.enter_subsection ("Solve");
solve_system_1();
timer.leave_subsection();
timer.enter_subsection ("Assemble");
assemble_system_2();
timer.leave_subsection();
timer.enter_subsection ("Solve");
solve_system_2();
timer.leave_subsection();
// do something else...
@ wall_times
Definition timer.h:753

When run, this program will return an output like this:

+---------------------------------------------+------------+------------+
| Total wallclock time elapsed since start | 88.8s | |
| | | |
| Section | no. calls | wall time | % of total |
+---------------------------------+-----------+------------+------------+
| Assemble | 2 | 19.7s | 22% |
| Solve | 2 | 3.03s | 3.4% |
| Setup dof system | 1 | 3.97s | 4.5% |
+---------------------------------+-----------+------------+------------+

The output shows that we entered the assembly and solve section twice, and reports how much time we spent there. Moreover, the class measures the total time spent from start to termination of the TimerOutput object. In this case, we did a lot of other stuff, so that the time proportions of the functions we measured are far away from 100 percent.

Using scoped timers

The scheme above where you have to have calls to TimerOutput::enter_subsection() and TimerOutput::leave_subsection() is awkward if the sections in between these calls contain return statements or may throw exceptions. In that case, it is easy to forget that one nevertheless needs to leave the section somehow, somewhere. An easier approach is to use "scoped" sections. This is a variable that when you create it enters a section, and leaves the section when you destroy it. If this is a variable local to a particular scope (a code block between curly braces) and you leave this scope due to a return statements or an exception, then the variable is destroyed and the timed section is left automatically. Consequently, we could have written the code piece above as follows, with exactly the same result but now exception-safe:

{
TimerOutput::Scope timer_section(timer, "Setup dof system");
setup_dofs();
}
{
TimerOutput::Scope timer_section(timer, "Assemble");
assemble_system_1();
}
{
TimerOutput::Scope timer_section(timer, "Solve");
solve_system_1();
}
{
TimerOutput::Scope timer_section(timer, "Assemble");
assemble_system_2();
}
{
TimerOutput::Scope timer_section(timer, "Solve");
solve_system_2();
}
// do something else...

Usage in parallel programs using MPI

In a parallel program built on MPI, using the class in a way such as the one shown above would result in a situation where each process times the corresponding sections and then outputs the resulting timing information at the end. This is annoying since you'd get a lot of output – one set of timing information from each processor.

This can be avoided by only letting one processor generate screen output, typically by using an object of type ConditionalOStream instead of std::cout to write to screen (see, for example, step-17, step-18, step-32 and step-40, all of which use this method).

This way, only a single processor outputs timing information, typically the first process in the MPI universe. However, if you take the above code snippet as an example, imagine what would happen if setup_dofs() is fast on processor zero and slow on at least one of the other processors; and if the first thing assemble_system_1() does is something that requires all processors to communicate. In this case, on processor zero, the timing section with name "Setup dof system" will yield a short run time on processor zero, whereas the section "Assemble" will take a long time: not because assemble_system_1() takes a particularly long time, but because on the processor on which we time (or, rather, the one on which we generate output) happens to have to wait for a long time till the other processor is finally done with setup_dofs() and starts to participate in assemble_system_1(). In other words, the timing that is reported is unreliable because it reflects run times from other processors. Furthermore, the run time of this section on processor zero has nothing to do with the run time of the section on other processors but instead with the run time of the previous section on another processor.

The first way to avoid this is to introduce a barrier into the parallel code just before we start and stop timing sections. This ensures that all processes are at the same place and the timing information then reflects the maximal run time across all processors. To achieve this, you need to initialize the TimerOutput object with an MPI communicator object, for example as in the following code:

TimerOutput timer (MPI_COMM_WORLD,
pcout,

Here, pcout is an object of type ConditionalOStream that makes sure that we only generate output on a single processor. See the step-32, step-40, and step-42 tutorial programs for this kind of usage of this class.

The second variant to cope with this issue is print more information about the recorded times to be able to understand this kind of imbalances without actually adding the barriers. While this approach is still affected by imbalances between different MPI processes, its output is not the arbitrary time of rank 0, but the minimum, average and maximum of the MPI results, using information from Utilities::MPI::MinMaxAvg. As the data is also equipped with the rank id where the minimum and maximum are attained, this approach allows to identify on which ranks certain slowdowns occur. In case some imbalance between the MPI ranks from one section to the next can be tolerated, this strategy can hence be advantageous over the barrier variant as it does not synchronize the program in places where it is not necessary, and rather tries to display the imbalance observed in various phases. In order to use this variant initialize the output object without any native print settings and without communicator,

and then call

timer.print_wall_time_statistics(MPI_COMM_WORLD);

where appropriate. Here, the output is written to the pcout object of type ConditionalOStream passed to the constructor, making sure the information is only printed once. See step-67 for an example usage of this variant. Besides the basic minimum, average, and maximum of times over all MPI ranks, the TimerOutput::print_wall_time_statistics() function also takes a second argument to specify output of quantiles, e.g., the time taken by the 10% of the slowest and fastest ranks, respectively, to get additional insight into the statistical distribution.

Definition at line 649 of file timer.h.

Classes

class  Scope
 

Public Types

enum  OutputFrequency { every_call , summary , every_call_and_summary , never }
 
enum  OutputData { total_cpu_time , total_wall_time , n_calls }
 
enum  OutputType { cpu_times , wall_times , cpu_and_wall_times , cpu_and_wall_times_grouped }
 

Public Member Functions

 TimerOutput (std::ostream &stream, const OutputFrequency output_frequency, const OutputType output_type)
 
 TimerOutput (ConditionalOStream &stream, const OutputFrequency output_frequency, const OutputType output_type)
 
 TimerOutput (const MPI_Comm mpi_communicator_timing, std::ostream &stream, const OutputFrequency output_frequency, const OutputType output_type)
 
 TimerOutput (const MPI_Comm mpi_communicator_timing, ConditionalOStream &stream, const OutputFrequency output_frequency, const OutputType output_type)
 
 ~TimerOutput ()
 
void enter_subsection (const std::string &section_name)
 
void leave_subsection (const std::string &section_name="")
 
std::map< std::string, double > get_summary_data (const OutputData kind) const
 
void print_summary () const
 
void print_wall_time_statistics (const MPI_Comm mpi_communicator_statistics, const double print_quantile=0.) const
 
void disable_output ()
 
void enable_output ()
 
void reset ()
 

Private Attributes

OutputFrequency output_frequency
 
OutputType output_type
 
Timer timer_all
 
std::map< std::string, Timer > sections
 
ConditionalOStream out_stream
 
bool output_is_enabled
 
std::list< std::string > active_sections
 
std::optional< MPI_Comm > mpi_communicator_timing
 
Threads::Mutex mutex
 

Member Enumeration Documentation

◆ OutputFrequency

An enumeration data type that describes whether to generate output every time we exit a section, just in the end, both, or never.

Enumerator
every_call 

Generate output after every call.

summary 

Generate output in summary at the end.

every_call_and_summary 

Generate output both after every call and in summary at the end.

never 

Never generate any output.

Definition at line 700 of file timer.h.

◆ OutputData

An enumeration data type that describes the type of data to return when fetching the data from the timer.

Enumerator
total_cpu_time 

Output CPU times.

total_wall_time 

Output wall clock times.

n_calls 

Output number of calls.

Definition at line 724 of file timer.h.

◆ OutputType

An enumeration data type that describes whether to show CPU times, wall times, or both CPU and wall times whenever we generate output.

Enumerator
cpu_times 

Output CPU times.

wall_times 

Output wall clock times.

cpu_and_wall_times 

Output both CPU and wall clock times in separate tables.

cpu_and_wall_times_grouped 

Output both CPU and wall clock times in a single table.

Definition at line 744 of file timer.h.

Constructor & Destructor Documentation

◆ TimerOutput() [1/4]

TimerOutput::TimerOutput ( std::ostream &  stream,
const OutputFrequency  output_frequency,
const OutputType  output_type 
)

Constructor not taking an MPI communicator as input. Timers managed by this class are default-constructed and do not require communication for synchronization in MPI-parallel computations. This corresponds to the second variant described in the documentation of this class.

Parameters
streamThe output stream (of type std::ostream) to which results are written.
output_frequencySpecifies how often output is written to the stream.
output_typeSpecifies the type of timing to report (CPU or wall time).

Definition at line 383 of file timer.cc.

◆ TimerOutput() [2/4]

TimerOutput::TimerOutput ( ConditionalOStream &  stream,
const OutputFrequency  output_frequency,
const OutputType  output_type 
)

Same as above, but accepts a ConditionalOStream.

Definition at line 397 of file timer.cc.

◆ TimerOutput() [3/4]

TimerOutput::TimerOutput ( const MPI_Comm  mpi_communicator_timing,
std::ostream &  stream,
const OutputFrequency  output_frequency,
const OutputType  output_type 
)

Constructor that additionally takes an MPI communicator. Timers managed by this class are initialized with mpi_communicator_timing and sync_lap_times = true. This ensures synchronization via global communication in MPI-parallel computations. CPU times are accumulated across all processes, while the reported wall time corresponds to the maximum observed over all processes in the given mpi_communicator_timing. This corresponds to the first variant described in this class documentation.

Definition at line 411 of file timer.cc.

◆ TimerOutput() [4/4]

TimerOutput::TimerOutput ( const MPI_Comm  mpi_communicator_timing,
ConditionalOStream &  stream,
const OutputFrequency  output_frequency,
const OutputType  output_type 
)

Same as above but for an ConditionalOStream.

Definition at line 426 of file timer.cc.

◆ ~TimerOutput()

TimerOutput::~TimerOutput ( )

Destructor. Calls print_summary() in case the option for writing the summary output is set.

Definition at line 441 of file timer.cc.

Member Function Documentation

◆ enter_subsection()

void TimerOutput::enter_subsection ( const std::string &  section_name)

Open a section by given a string name of it. In case the name already exists, that section is entered once again and times are accumulated.

Definition at line 489 of file timer.cc.

◆ leave_subsection()

void TimerOutput::leave_subsection ( const std::string &  section_name = "")

Leave a section. If no name is given, the last section that was entered is left.

Definition at line 526 of file timer.cc.

◆ get_summary_data()

std::map< std::string, double > TimerOutput::get_summary_data ( const OutputData  kind) const

Get a map with the collected data of the specified type for each subsection

Definition at line 591 of file timer.cc.

◆ print_summary()

void TimerOutput::print_summary ( ) const

Print a formatted table that summarizes the time consumed in the various sections.

Definition at line 622 of file timer.cc.

◆ print_wall_time_statistics()

void TimerOutput::print_wall_time_statistics ( const MPI_Comm  mpi_communicator_statistics,
const double  print_quantile = 0. 
) const

Print a formatted table that summarizes the wall time consumed in the various sections, using statistics in terms of the minimum, average, and maximum of times in the various sections and the MPI ranks where the minimum and maximum are attained. Note that this call only provides useful information when the TimerOutput object is constructed without an MPI communicator, allowing individual sections to run without being disturbed by barriers. Calling this function in this case is still valid, but requires mpi_communicator_statistics to contain the same processes as the MPI communicator used to construct the TimerOutput. If the TimerOutput is constructed without an MPI communicator ranks within the provided mpi_communicator_statistics are not required to enter the same sections; in this case, an appropriate zero timing will be reported for not entered sections.

The optional argument quantile allows to add two additional columns to the output in terms of the distribution of run times. If quantile = 0.1, the value and rank of the 10% lowest data is printed as well as the value and rank at 90% of the distribution function, in addition to the minimum and the maximum. The value of quantile needs to be between 0 (no quantiles are printed besides the minimum and maximum) and 0.5 (when the median is given).

Definition at line 936 of file timer.cc.

◆ disable_output()

void TimerOutput::disable_output ( )

By calling this function, all output can be disabled. This function together with enable_output() can be useful if one wants to control the output in a flexible way without putting a lot of if clauses in the program.

Definition at line 1162 of file timer.cc.

◆ enable_output()

void TimerOutput::enable_output ( )

This function re-enables output of this class if it was previously disabled with disable_output(). This function together with disable_output() can be useful if one wants to control the output in a flexible way without putting a lot of if clauses in the program.

Definition at line 1171 of file timer.cc.

◆ reset()

void TimerOutput::reset ( )

Resets the recorded timing information.

Definition at line 1180 of file timer.cc.

Member Data Documentation

◆ output_frequency

OutputFrequency TimerOutput::output_frequency
private

When to output information to the output stream.

Definition at line 896 of file timer.h.

◆ output_type

OutputType TimerOutput::output_type
private

Whether to show CPU times, wall times, or both CPU and wall times.

Definition at line 901 of file timer.h.

◆ timer_all

Timer TimerOutput::timer_all
private

A timer object for the overall run time. If we are using MPI, this timer also accumulates over all MPI processes.

Definition at line 908 of file timer.h.

◆ sections

std::map<std::string, Timer> TimerOutput::sections
private

A list of all the sections and their information.

Definition at line 913 of file timer.h.

◆ out_stream

ConditionalOStream TimerOutput::out_stream
private

The stream object to which we are to output.

Definition at line 918 of file timer.h.

◆ output_is_enabled

bool TimerOutput::output_is_enabled
private

A boolean variable that sets whether output of this class is currently on or off.

Definition at line 924 of file timer.h.

◆ active_sections

std::list<std::string> TimerOutput::active_sections
private

A list of the sections that have been entered and not exited. The list is kept in the order in which sections have been entered, but elements may be removed in the middle if an argument is given to the leave_subsection() function.

Definition at line 932 of file timer.h.

◆ mpi_communicator_timing

std::optional<MPI_Comm> TimerOutput::mpi_communicator_timing
private

An optional mpi communicator used to synchronize the timing results across MPI processes.

Definition at line 938 of file timer.h.

◆ mutex

Threads::Mutex TimerOutput::mutex
private

A lock that makes sure that this class gives reasonable results even when used with several threads.

Definition at line 944 of file timer.h.


The documentation for this class was generated from the following files: