Skip to content

Timers

Timers enable one to get the computation time of a specific task. For example, one would want to know the computation time to extract data in files. To get this information one can create a timer. In the following paragraphs, we will present the extent of the timers structure that contains a set of timers (of type single_timer).

First, we will present the parametrization of a single timer single_timer.

integer(ip), parameter :: max_timers = 100_ip
TYPE single_timer
character(len=:), allocatable :: label
integer(8) :: nb_calls = 0_8
real(rp) :: total = 0._rp
real(rp) :: mean = 0._rp
real(rp) :: variance = 0._rp
real(rp) :: min = huge( 0._rp )
real(rp) :: max = 0._rp
integer(8) :: s
integer(8) :: e
logical(lp) :: started = .false.
END TYPE single_timer

A timer focuses on the synthetization of one task’s computation time. A timer (from the structure single_timer) defines the time spent on a task by :

  • nb_calls : number of times the task is executed, or more specificly the number of times the timer is started and stopped.
  • total : the total time spent on the task (cumulative time)
  • mean : the average time spent on the task
  • variance : the variance of the time spent on the task
  • min : the minimum time spent on the task
  • max : the maximum time spent on the task
  • s : saves the system’s time when the timer is started
  • e : saves the system’s time when the timer is stopped
  • started : logical parameter to define if the timer has been started or not

Usually, one would want to check the time spent on several tasks. Therefore, a timers structure has been created to manage all the timers linked to all the reviewed tasks.

TYPE timers
character(len=:), allocatable :: name
logical :: stopped = .false.
integer(ip) :: nb_total = 0_ip
integer(ip) :: nb_nolabel = 0_ip
integer(ip) :: active = 0_ip
integer(ip) :: master = 0_ip
real(rp) :: rate
type(single_timer) :: timer( max_timers )
CONTAINS
procedure, pass :: init => timers_init
procedure, pass :: start => timers_start
procedure, pass :: stop => timers_stop
procedure, pass :: tic => timers_tic
procedure, pass :: toc => timers_toc
procedure, pass :: report => timers_report
procedure, pass :: setmaster => timers_set_master
procedure, pass :: reset => timers_reset
procedure, pass :: allreset => timers_allreset
procedure, pass :: allstart => timers_allstart
procedure, pass :: allstop => timers_allstop
END TYPE timers

This structure timers contains a table of timers to analyse each task. This structure contains the number of timers nb_total, the index nb_nolabel of the timer if the timer has no label, the index active of the active timer, and the index of the master timer master. The variable stopped only checks if all the timers are stopped. The rate determines the number of the processor’s clock ticks per second.

To use a timer to track a task, one first to initialize it by calling the timers_init subroutine.

SUBROUTINE timers_init( self , label , timerid , ismaster )
class(timers) , intent(inout) :: self
character(len=*), intent(in ), optional :: label
integer(ip) , intent( out), optional :: timerid
logical , intent(in ), optional :: ismaster
END SUBROUTINE timers_init

One initialize a timer by eventually specifying its label and if it is a master timer. If one wishes to get the timer’s index in the timers table, one has to give the output variable timerid stocking this index.

When the timer is initialized, one can start the timer by calling timers_start and specifying its index in the timers table or its label.

SUBROUTINE timers_start( self , timerid , label )
class(timers) , intent(inout) :: self
integer(ip) , intent(in ), optional :: timerid
character(len=*), intent(in ), optional :: label
END SUBROUTINE timers_start

When the studied task is over, one can stop the timer and know the time spent on this task by calling timers_stop and specifying the timer’s index or label.

SUBROUTINE timers_stop( self , label , timerid )
class(timers) , intent(inout) :: self
character(len=*), intent(in ), optional :: label
integer(ip) , intent(in ), optional :: timerid
END SUBROUTINE timers_stop

One can also get the time spent on successive small tasks by calling timers_tic at the beginning of each small task. If one wishes to have a timer report for each small task, one should specify withprint = .true.. The timer is stopped by calling timers_toc or by starting a new timer (the new timer will be the active one).

SUBROUTINE timers_tic( self , label , withprint )
class(timers) , intent(inout) :: self
character(len=*), intent(in ), optional :: label
logical , intent(in ), optional :: withprint
END SUBROUTINE timers_tic
SUBROUTINE timers_toc( self )
class(timers), intent(inout) :: self
END SUBROUTINE timers_toc

The timers_allstart and timers_allstop routines just switch stopped to .false. or .true..

SUBROUTINE timers_allstart( self )
class(timers), intent(inout) :: self
END SUBROUTINE timers_allstart
SUBROUTINE timers_allstop( self )
class(timers), intent(inout) :: self
END SUBROUTINE timers_allstop

The following paragraph will provide an example of the timers structure use. First, one needs to initialize the required timers. For example, one wishes to know the total simulation time, the time spent on MPI communications, the time spent on the computation of one time step, the time spent on boundary conditions’ computation, etc.

type(timers) :: timer
integer :: timer_id(100)
[...]
call timer%init( label='Time of simulation' , timerid=timer_id(1) )
call timer%init( label='MPI_SEND_RECV' , timerid=timer_id(2) )
call timer%init( label='Time Step Computation' , timerid=timer_id(3) )
call timer%init( label='Boundary Conditions' , timerid=timer_id(4) )
[...]

To get the time spent on a task, one has to start a timer at the beginning of each task’s execution. Here for example, the timer ‘Time of simulation’ will be started at the beginning of the program and stopped at the end of the program, but the timer ‘MPI_SEND_RECV’ will be started at the beginning of each MPI communication and stopped at the end of each communication.

type(timers) :: timer
integer :: timer_id(100)
[...]
call timer%start( label='MPI_SEND_RECV' ) ! Timer identified by its label
[...] ! MPI communication
call timer%stop( timerid=timer_id(2) ) ! Timer identified by its id

For example, still concerning MPI communications, one can also track the time spent on successive MPI communications :

type(timers) :: mpi_timer
call mpi_timer%tic( 'First MPI communication', withprint=.true. )
[...] ! First MPI communication
call mpi_timer%tic( 'Second MPI communication', withprint=.true. )
[...] ! Second MPI communication
[...]
call mpi_timer%tic( 'Last MPI communication', withprint=.true. )
[...] ! Last MPI communication
call mpi_timer%toc
call mpi_timer%report

The report subroutine prints a report of the timers’ informations concerning the time spent on various tasks.