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).

Timers' structure

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
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.

19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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.

1
2
3
4
5
6
7
8
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.

Set timer master

If one wants to set a timer as the timer master, one can call the timers_set_master subroutine separately from the initialization.

1
2
3
4
5
6
SUBROUTINE timers_set_master( self , timerid )

    class(timers), intent(inout) :: self
    integer(ip)  , intent(in   ) :: timerid

END SUBROUTINE timers_set_master

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.

1
2
3
4
5
6
7
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.

1
2
3
4
5
6
7
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

?? NOT SURE

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).

??

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
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

Reset

One can also reset the information concerning a timer by calling timers_reset and specifying the timer's label or index. The timers_allreset subroutine resets all timers.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
SUBROUTINE timers_reset( self , timerid , label )

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

END SUBROUTINE timers_reset

SUBROUTINE timers_allreset( self )

    class(timers), intent(inout) :: self

END SUBROUTINE timers_allreset

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
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

Usage

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

Tic ...

One does not need to initialize each timer when using the tic subroutine.

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

Back to top