Manual Reference Pages  - M_STOPWATCH (3)

NAME

M_stopwatch(3f)(3f) - [M_stopwatch] package for measuring cpu and wall clock execution time (LICENSE:PD)

CONTENTS

Synopsis
Description
Diagnostics
Example
Alternatives
Bugs
Author
See Also

SYNOPSIS

M_StopWatch is a Fortran 90 module for measuring execution time of program segments. M_StopWatch is designed to be a portable, easy-to-use means of measuring execution time. It supports the wall clock, CPU clock, and a breakdown of the CPU clock into user and system times. It returns all times in seconds. It provides a simple means of determining which clocks are available, and the precision of those clocks.

M_StopWatch is used by instrumenting your code with subroutine calls that mimic the operation of a stop watch. The primary routines are start_watch, stop_watch, reset_watch, read_watch, and print_watch. M_StopWatch supports multiple watches, and provides the concept of watch groups to allow functions to operate on multiple watches simultaneously.

DESCRIPTION

The entities in M_StopWatch that have public accessibility are two derived types and fifteen subroutines (one of which is a function subroutine). Any program unit that references any of these entities must use the M_stopwatch module, i.e., must contain the statement

use M_stopwatch

The derived types are:

o watchtype -- used for declaring a variable to be a watch

o watchgroup -- used for declaring a variable to be a handle for a group of watches

These two types have public accessibility, but the internals of the type are private. Any operations performed on a variable of one of these types must be performed by one of the M_StopWatch subroutines.

This man page describes, in general terms, the operations that can be performed by the M_StopWatch subroutines. The formal interfaces and detailed descriptions of the routines can be found in the individual man pages.

A watch is a variable declared to be of type watchtype. It can be passed to subroutines as an actual argument or through modules like any Fortran variable, but can only be operated on by the M_StopWatch subroutines. Watches must be created by subroutine create_watch before they are used. Attempting to use a watch that has not been created will generate a Fortran 90 error, from attempting to pass a pointer with undefined association status to the Fortran intrinsic function associated. Watches must be destroyed when no longer useful. For example, consider a local variable of type watchtype in a subroutine. Since the contents of a local variable are lost when the subroutine returns, the watch should be destroyed before returning to the calling program. Failure to destroy watches can lead to a memory leak.

o create_watch -- creates a watch

o destroy_watch -- destroys a watch

Watches can optionally be given a name (up to 132 characters) through an optional argument, name, in create_watch. This name is used in error messages and print_watch to identify the watch in the printed output.

Different applications demand different definitions of ‘‘time’’. M_StopWatch supports four clocks in each watch, with each clock measuring a different concept of time. All of them measure time in seconds.

o user -- the amount of CPU time used by the user’s program

o sys -- the amount of CPU time used by the system in support of the user’s program

o cpu -- the total CPU time, i.e., user+sys

o wall -- the wall clock time, i.e., elapsed real time

It is not required that all clocks be used. A watch can be created with any combination of the four clocks. You can also specify a set of default clocks to be used whenever the clocks are not explicitly determined.

Since Fortran 90 does not contain an intrinsic function for CPU time, the implementation of the cpu, sys and user clocks is system dependent. Some implementations may support only cpu and wall, not user and sys. Some implementations may support only wall. Since the Fortran 90 standard requires the existence of a system_clock subroutine, but does not require that it provide clock information, it is possible that some implementations might not support wall. Clock availability can be determined by inquiry_stopwatch. Unavailable clocks will automatically be removed from the set of default clocks, but if a clock that is not available is explicitly requested, a warning message will be generated.

M_StopWatch supports multiple watches simultaneously. Often it is useful to perform the same operation on several watches. This is essential for correct operation of pause_watch and end_pause_watch, and can also be convenient for procedures like read_watch, print_watch, and reset_watch. To facilitate this, M_StopWatch supports the concept of watch groups. When calling a M_StopWatch subroutine, a watch group can be specified instead of a watch. The group is referenced by a variable of type watchgroup. Watch groups must be created before they are used. Attempting to use a watch group that has not been created will generate a Fortran 90 error, from attempting to pass a pointer with undefined association status to the Fortran intrinsic function associated. Watch groups must be destroyed when no longer useful. The watches themselves are not destroyed, only the grouping of them. Failure to destroy watch groups can lead to a memory leak.

o create_watchgroup -- creates a new watch group

o destroy_watchgroup -- destroys a watch group (but not the watches in the group)

Most M_StopWatch subroutines take watch as the first dummy argument, and accept several forms of watch. The forms are:

o type (watchtype) watch -- a single watch

o type (watchtype) watch(:) -- an array of watches

o type (watchgroup) watch -- a watch group handle

In most M_StopWatch routines, an array of watches can be specified by an array constructor in the calling statement, for example:

type (watchtype) watch :: w1, w2, w3

call print_watch( (/w1,w2,w3/) )

However, this can not be used in routines where watch has intent OUT or intent INOUT, because the array constructor is actually an expression, not a list of the variables. Currently this includes the routines create_watch and destroy_watch.

Most M_StopWatch subroutines take clock as the (optional) second dummy argument to determine which of the four clocks will be affected by the action. clock can be one of the character strings ’user’, ’sys’, 'cpu’, or ’wall’, or can be an array of such character strings to specify more than one clock. Since clock is always intent IN, an array of clock types can be built with an array constructor. However, note that Fortran 90 requires all character strings in such a construction to have the same length. Thus ’sys’ and 'cpu’ should be padded with a blank, as in:

call start_watch(watch, (/’user’,’sys ’,’cpu ’/) )

If the optional argument clock is omitted, the current set of default clocks is used. The set of default clocks is set with option_stopwatch and initially consists of all available clocks.

A M_StopWatch watch is operated on by subroutine calls that correspond to the actions performed with a common stop watch. The basic operation of a watch involves starting it, stopping it, and resetting it to 0.

o start_watch -- starts an idle watch, like the Start/Stop button on a stop watch

o stop_watch -- stops a running watch, like the Start/Stop button on a stop watch

o reset_watch -- sets the clocks on a watch to 0.0, like the Reset button on a stop watch

Of course, running a stop watch is of little use unless you can see what it says.

o read_watch -- returns the current clock value of a watch, like looking at the display of a stop watch

o print_watch -- prints the current clock value of a watch to an output device. To push the analogy to the limit, imagine a stop watch with a printer attached to it.

The routine read_watch is a function subroutine. The result is either a scalar, array of rank one, or array of rank two depending on whether watch and clock are scalars or arrays.

When measuring CPU time, it is often desirable to not include the time used by certain parts of the code, such as printing or graphics. In a subroutine, you might not know which of the clocks are currently running, so you can not simply stop them before the I/O and start them up again after the I/O. For this, M_StopWatch provides the pause function.

o pause_watch -- temporarily suspend the running watches

o end_pause_watch -- resume suspended watches

Besides create_watchgroup and destroy_watchgroup, there are two operations that can be performed on watchgroup variables:

o join_watchgroup -- adds a watch to a watch group

o leave_watchgroup -- removes a watch from a watch group

Subroutines are provided to set several options within M_StopWatch, to determine the current value of these options, and to determine system dependent values of the implementation.

o option_stopwatch -- sets options within M_StopWatch.

o inquiry_stopwatch -- returns values of options and system dependent values

See option_stopwatch(3) and inquiry_stopwatch(3) for a description of the options and system dependent values that can be set and read.

DIAGNOSTICS

All M_StopWatch subroutines take an optional intent OUT integer argument err which, if present, returns a status code. The code is the sum of the values listed below.

Errors can also be determined through printed error messages. An error message will be printed to a specified I/O unit (unit 6 by default) if print_errors is TRUE (default is TRUE). The error message contains more detail about the cause of the error than can be obtained from just the status code, so you should set print_errors to TRUE if you have trouble determining the cause of the error.

All errors are non-fatal. If abort_errors is TRUE (default is FALSE), the program will terminate on an error condition. Otherwise, the program will continue execution but the requested operation will not be performed.

See option_stopwatch(3) for further information on print_errors, abort_errors and I/O units.

The relevant status codes and messages are:

0 No errors; execution successful.

1 Watch needs to be created. This error occurs if you attempt to use a watch that has been destroyed. The watch must first be created again. See also the comment about watches that have never been created in the BUGS section.

2 Watch is in the wrong state for this operation. This occurs when you attempt to start a watch that is currently running, stop a watch that is not running, etc.

4 Watch is in an unknown state. This occurs if M_StopWatch does not recognize the state (running, stopped, etc.) that the watch is in. This error should not occur, and indicates an internal bug in M_StopWatch.

8 Invalid clock type. This occurs if clock is present and one of the specified clocks is not supported by the implementation. See inquiry_stopwatch(3) to determine what clocks are available.

16 Too many clocks specified. This occurs when the argument clock is an array longer than four.

32 Number of names is not equal to number of watches. This occurs in create_watch if the array of watch names is not of the same length as the array of watches.

64 Character string too long. This occurs when a watch name with more than 132 characters is passed into create_watch.

128 Watch not found in given group. This occurs when you attempt to remove a watch from a group that it does not belong to.

256 I/O unit is not open for writing. This can occur from print_watch or when printing an error message.

512 Failed to allocate required memory. When a M_StopWatch routine is called with an array or group of watches, temporary memory is allocated. This error occurs if the Fortran allocate statement returns a nonzero status indicating that memory could not be allocated. Avoid memory leaks by always destroying watches and groups before recreating them, and destroying local variable watches and groups before returning from a subroutine.

1024 Error occurred while deallocating memory. This error occurs if the Fortran deallocate statement returns a nonzero status while deallocating temporary memory used for an array or group of watches. The operation is performed, but be aware that other problems could develop as a result of the deallocate error.

2048 Illegal output form. This error occurs in option_stopwatch or print_watch if the given print format is not one of the valid strings (see print_watch(3) ).

In addition to the run time diagnostics generated by M_StopWatch, the following problems may arise:

o In Fortran 90, the character strings in an array constructor must all have the same length. Pad three letter clock names with a blank on the right to make a four character string, for example, ’cpu ’, and pad watch names so they all have the same length (within an array constructor).

o In create_watch and destroy_watch, watch has intent OUT or INOUT, so you cannot use an array constructor as an actual argument to construct an array of watches. Some compilers will recognize this as a compile time error, but will generate an obscure error message, such as ‘‘no specific match for generic name’’.

EXAMPLE

! program demo_M_stopwatch and modules ! The following times are measured: ! ! o each phase each time through the loop ! o total time for each phase ! o the total time ! ! There will be printed output that should not be included in the ! measured time. ! module globals use M_stopwatch implicit none private public :: watchgroup public :: setup_watches

! the group with all the watches; global var type (watchgroup), public :: ALL_GROUPS

type (watchtype), public, dimension(5) :: w ! The watches are: w(1) time for phase 1 this time through the loop ! w(2) time for phase 2 this time through the loop ! w(3) total time for phase 1 ! w(4) total time for phase 2 ! w(5) total time ! The watch groups are: GROUPS_FOR_ONE phase 1 times w(1) and w(3) ! GROUPS_FOR_TWO phase 2 times w(2) and w(4) ! ALL_GROUPS all of them (declared in module globals) type (watchgroup), public :: GROUPS_FOR_ONE, GROUPS_FOR_TWO contains subroutine setup_watches

! Measure only cpu and wall time call option_stopwatch(default_clock=(/"cpu ","wall"/)) call create_watch(w,name=(/ "phase 1 ", & ! create the watches "phase 2 ", & "total phase 1", & "total phase 2", & "Total " /) ) call create_watchgroup(w(1),GROUPS_FOR_ONE) ! create the groups call join_watchgroup(w(3),GROUPS_FOR_ONE) call create_watchgroup(w(2:4:2),GROUPS_FOR_TWO) ! a shorter way call create_watchgroup(w,ALL_GROUPS) call start_watch(w(5)) ! start the total time

end subroutine setup_watches

end module globals

module workers implicit none ! The routines being measured public :: subone contains

subroutine subone(n,c) ! just to give us something to time. use M_stopwatch use globals integer, intent(in) :: n real, intent(out) :: c integer :: i real :: a=2.0,b b=real(n) do i=1,n c=a*b end do

call pause_watch(ALL_GROUPS) write(unit=*,fmt=*) "Performed ",n," multiplications" call end_pause_watch(ALL_GROUPS)

end subroutine subone

end module workers

program demo_M_stopwatch use M_stopwatch use globals use workers implicit none

integer :: i, nmult ! loop counter, number of multiplies to do logical :: cpu_is_there ! flag for cpu clock real :: zz

call setup_watches()

nmult = 200000 do i=1,3 write(*,’(a)’)repeat(’=’,70) write(*,*)’LOOP’,i,’:’ ! reset the watches that measure the time for this loop call reset_watch(w(1:2)) ! start the phase 1 watches, do phase 1, and stop the phase 1 watches call start_watch(GROUPS_FOR_ONE) nmult = 5*nmult call subone(nmult,zz) call stop_watch(GROUPS_FOR_ONE)

call start_watch(GROUPS_FOR_TWO) ! same for phase 2 nmult = 2*nmult call subone(nmult,zz) call stop_watch(GROUPS_FOR_TWO)

! pause the cpu clock of the total time watch while printing the current times, ! if the cpu clock is available on this implementation, but leave the wall ! clock running. The call to inquiry_stopwatch should be outside the loop, but ! this should make a clearer illustration.

call inquiry_stopwatch(cpu_avail=cpu_is_there) if (cpu_is_there) then call pause_watch(w(5),"cpu") end if

write(*,’(a)’)repeat(’-’,70) call print_watch(w(1:2),title="Times for this loop") write(*,’(a)’)repeat(’-’,70) call print_watch(w(3:4),title="Total times so far")

if (cpu_is_there) then call end_pause_watch(w(5),"cpu") end if

end do

write(*,’(a)’)repeat(’=’,70) ! print the total times call print_watch([w(3),w(4),w(5)],title="Final total times")

write(unit=*,fmt=*)& &"Note: the difference between the sum of the first two wall clocks" write(unit=*,fmt=*)& &" and the Total wall clock is due to not pausing the wall clock" write(unit=*,fmt=*)& &" on the Total watch while printing."

call destroy_watch(w) ! destroy the watches end program demo_M_stopwatch

Expected output:

======================================================================
 LOOP           1 :
 Performed      1000000  multiplications
 Performed      2000000  multiplications
----------------------------------------------------------------------
Times for this loop
  phase 1:
        cpu=    0.00 wall=    0.00
  phase 2:
        cpu=    0.01 wall=    0.02
----------------------------------------------------------------------
Total times so far
  total phase 1:
        cpu=    0.00 wall=    0.00
  total phase 2:
        cpu=    0.01 wall=    0.02
======================================================================
 LOOP           2 :
 Performed     10000000  multiplications
 Performed     20000000  multiplications
----------------------------------------------------------------------
Times for this loop
  phase 1:
        cpu=    0.05 wall=    0.05
  phase 2:
        cpu=    0.11 wall=    0.11
----------------------------------------------------------------------
Total times so far
  total phase 1:
        cpu=    0.05 wall=    0.05
  total phase 2:
        cpu=    0.12 wall=    0.12
======================================================================
 LOOP           3 :
 Performed    100000000  multiplications
 Performed    200000000  multiplications
----------------------------------------------------------------------
Times for this loop
  phase 1:
        cpu=    0.49 wall=    0.48
  phase 2:
        cpu=    0.97 wall=    0.99
----------------------------------------------------------------------
Total times so far
  total phase 1:
        cpu=    0.53 wall=    0.53
  total phase 2:
        cpu=    1.09 wall=    1.11
======================================================================
Final total times
  total phase 1:
        cpu=    0.53 wall=    0.53
  total phase 2:
        cpu=    1.09 wall=    1.11
  Total:
        cpu=    1.62 wall=    1.64
 Note: the difference between the sum of the first two wall clocks
       and the Total wall clock is due to not pausing the wall clock
       on the Total watch while printing.

ALTERNATIVES

If you simply need to time a certain portion of the code and do not wish to use the module the SYSTEM_CLOCK(3f) intrinsic may be sufficient:

   :
   :
call system_clock(count_rate=clock_rate) !Find the time rate
call system_clock(count=clock_start)     !Start Timer

call do_something_subroutine !This is what gets timed

call system_clock(count=clock_stop) ! Stop Timer e_time = real(clock_stop-clock_start)/real(clock_rate) : :

For full timing analysis during the development cycle programming environments generally provide a profiler tool.

If you use the GNU compiler, check out gprof. In short, you’ll add the -pg flag to your compiler, like so:

gfortran -g -pg -0 myprogram myprogram.F
Then, run the program and a file called "gmon.out" will be generated. Then, for example, call
gprof --line myprogram gmon.out
This will give a line-by-line CPU time profile.

MPI_WTIME: This is a high resolution wall-clock that will work anywhere MPI is available. The downside is that if your program doesn’t already use MPI, you’ll have to wrap MPI around it (which isn’t hard). Here is a simple example:

  include ’mpif.h’
  DOUBLE PRECISION :: start, end
  start = MPI_Wtime()

! code to be timed

end = MPI_Wtime() write(*,*) ’That took ’,end-start,’ seconds’

BUGS

It cannot be determined whether or not a watch variable or watch group has been created (passed as an argument to create_watch or create_watchgroup). If a watch or watch group that has never been created is passed into a M_StopWatch subroutine, it might generate a Fortran error due to passing a pointer with undefined association status to the Fortran intrinsic function associated. However, some compilers will allow this as an extension to the Fortran 90 standard and recognize that the pointer is not associated, in which case the ‘‘Watch needs to be created’’ error message is generated.

AUTHOR

V1.0: William F. Mitchell, william.mitchell@nist.gov -- January 3, 1997 National Institute of Standards and Technology V1.1: John S. Urban, urbanjost@comcast.net -- September 22, 2017

SEE ALSO

create_watchgroup(3), create_watch(3), destroy_watchgroup(3), destroy_watch(3), end_pause_watch(3), inquiry_stopwatch(3), join_watchgroup(3), leave_watchgroup(3), option_stopwatch(3), pause_watch(3), print_watch(3), read_watch(3), reset_watch(3), start_watch(3), stop_watch(3)


M_StopWatch 1.1 M_STOPWATCH (3) September 22, 2017
Generated by manServer 1.08 from M_stopwatch.3.txt using man macros.