next up previous index
Next: Restoring and Saving the Up: Timing a Job Previous: Timing a Job in

Timing a Job in Fortran

Even Fortran-90 does not provide portable means for checking CPU time usage without calling vendor and operating system specific routines.

Fortran 90 defines two intrinsic procedures date_and_time and system_clock, which return elapsed wall-clock time in various formats.

The date_and_time procedure takes 4 arguments, all of which are optional:

date: intent(out)
a character string at least 8 characters long
time: intent(out)
a character string at least 10 characters long
zone: intent(out)
a character string at least 5 characters long
values: intent(out)
an array of integers at least 8 entries long

For our purposes we don't need date or time returned as strings. We only need the numbers, which are returned in values, so we'll call this procedure using a keyword argument list:

call date_and_time (values=time_array)
where time_array is our array of integers. The returned values will have the following ordering:
month of the year
day of the month
time offset with respect to UTC in minutes
hour of the day
minutes of the hour
seconds of the minute
milliseconds of the second

Subroutine system_clock is somewhat easier to use. It takes 3 optional arguments:

count, intent(out)
an integer
count_rate: intent(out)
an integer
count_max: intent(out)
an integer

This function is somewhat similar to C-function clock, in the sense that it counts time at a rate of count_rate counts per second up to count_max, and then resets itself to zero and resumes the counting. But unlike clock this function measures wall-clock time, not the CPU time. As you will see from the following example, under AIX procedure system_clock resets every day at midnight. But this particular behaviour is not specified in F90 standard.

In fact there is no intrinsic Fortran-90 procedure for measuring CPU time. For that we have to use XL-Fortran service and utility function etime_. At this stage the program ceases to be portable, so it is a good idea to isolate the parts of the code that rely on etime_ with cpp #ifdef .. #endif brackets. In the example below I use gcc -E -P -C instead of cpp. It is important to remove cpp generated line references before passing the file to Fortran compiler. Option -P ensures that. The importance of option -C will become clearer in our next Fortran-90 example.

Function etime_ is defined in the xlfutility module, which must be included with the use statement:

use xlfutility
The function takes a structure of type tb_type as argument (intent(out)) and returns the sum of system and user components of the CPU time since the start of the execution of a process. Additionally user time and system time are written on usrtime and systime slots of the argument.

For more information about service and utility procedures provided in xlfutility read the ``XL Fortran for AIX, Language Reference, Version 3, Release 2'' manual, pages 445-451. The manual, in compressed PostScript, can be found in the /usr/lpp/xlf/ps directory on any SP node.

The following Fortran 90 program shows how to use all three procedures in order to time your computation.

      program f_time

#ifdef XLF
      use xlfutility

! Variables for function dtime_

      real(4) elapsed_0, elapsed_1
      type (tb_type) etime_struct_0, etime_struct_1

! Variables for subroutine system_clock

      integer count_0, count_1, count_rate, count_max

! Variables for subroutine date_and_time

      integer time_array_0(8), time_array_1(8)
      real start_time, finish_time

! Variables for computation

      integer n
      parameter (n = 1000000)
      double precision a(n), b(n), c(n)

      write (6, '(1x, 1a)') 'using F90 procedure date_and_time ...'
      write (6, '(1x, 1a)') 'using F90 procedure system_clock ...'
#ifdef XLF
      write (6, '(1x, 1a)') 'using XLF function dtime_ ...'
! Mark the beginning of the program

      call date_and_time(values=time_array_0)
      start_time = time_array_0 (5) * 3600 + time_array_0 (6) * 60 &
           + time_array_0 (7) + 0.001 * time_array_0 (8)
      call system_clock(count_0, count_rate, count_max)
#ifdef XLF
      elapsed_0 = etime_(etime_struct_0)

      write (6, '(8x, 1a, 1f16.6)') 'begin (date_and_time):  ', &
      write (6, '(8x, 1a, 1f16.6)') 'begin (system_clock):   ', &
           count_0 * 1.0 / count_rate
#ifdef XLF
      write (6, '(8x, 1a, 1f16.6)') 'begin (etime_%usrtime): ', &
      write (6, '(8x, 1a, 1f16.6)') 'begin (etime_%systime): ', &

! Sleep for 5 seconds

#ifdef XLF
      write (6, '(16x, 1a)') 'sleep for 5 seconds ... '
      call sleep_ (5)

! Perform some computation

      write (6, '(16x, 1a)') 'perform some computation ... '
      a = (/ (i, i = 1, n) /)
      a = sqrt(a)
      b = 1.0 / a
      c = b - a      

! Mark the end of the program

      call date_and_time(values=time_array_1)
      finish_time = time_array_1 (5) * 3600 + time_array_1 (6) * 60 &
           + time_array_1 (7) + 0.001 * time_array_1 (8)
      call system_clock(count_1, count_rate, count_max)
#ifdef XLF
      elapsed_1 = etime_(etime_struct_1)

      write (6, '(8x, 1a, 1f16.6)') 'end (date_and_time):    ', &
      write (6, '(8x, 1a, 1f16.6)') 'end (system_clock):     ', &
           count_1 * 1.0 / count_rate
#ifdef XLF
      write (6, '(8x, 1a, 1f16.6)') 'end (etime_%usrtime):   ', &
      write (6, '(8x, 1a, 1f16.6)') 'end (etime_%systime):   ', &

! Print elapsed time

      write (6, '(8x, 1a, 1f16.6)') 'elapsed wall clock time:', &
           finish_time - start_time           
#ifdef XLF
      write (6, '(8x, 1a, 1f16.6)') 'elapsed CPU time:       ', &
           etime_struct_1%usrtime - etime_struct_0%usrtime

      end program f_time

This file must be passed through cpp first in order to generate the plain Fortran code. Then the code must be compiled and linked with Fortran-90 compiler. The most convenient way to go about all that is to write appropriate instructions on a Makefile and use make to generate the binary. Here is the Makefile that you can use for this F90 example code:

F90     = xlf90
CPP     = gcc -E -P -C
OPTS    = # -g

all: f_time

f_time: f_time.o
        $(F90) $(OPTS) -o f_time f_time.o

f_time.o: f_time.f
        $(F90) $(OPTS) -c f_time.f

f_time.f: f_time.cpp
        $(CPP) $(DEFINES) f_time.cpp > f_time.f

        rm -f f_time.f f_time.o f_time
On the IU SP system I have compiled and run this program as follows:
<6:05:00 !705 $ gcc -E -P -C -DXLF f_time.cpp > f_time.f
gustav@sp19:../LoadLeveler 16:05:15 !706 $ xlf90 -o f_time f_time.f
** f_time   === End of Compilation 1 ===
1501-510  Compilation successful for file f_time.f.
gustav@sp19:../LoadLeveler 16:05:26 !707 $ time -p ./f_time
 using F90 procedure date_and_time ...
 using F90 procedure system_clock ...
 using XLF function dtime_ ...
        begin (date_and_time):      57937.957031
        begin (system_clock):       57937.949219
        begin (etime_%usrtime):         0.000000
        begin (etime_%systime):         0.010000
                sleep for 5 seconds ... 
                perform some computation ... 
        end (date_and_time):        57944.312500
        end (system_clock):         57944.308594
        end (etime_%usrtime):           0.850000
        end (etime_%systime):           0.210000
        elapsed wall clock time:        6.355469
        elapsed CPU time:               0.850000
real 6.43
user 0.85
sys 0.21
gustav@sp19:../LoadLeveler 16:05:44 !708 $
And, as before, we can see that our internal estimates agree pretty well with results returned by UNIX program time. There is a small discrepancy of 0.08 s in the estimate of the elapsed wall-clock time (6.35s versus 6.43s), the explanation of which is left to the reader as an exercise. Also observe that time returned by procedure system_clock is roughly the same as time returned by procedure date_and_time, which means that system_clock must be reset at midnight, as I have already remarked above.

next up previous index
Next: Restoring and Saving the Up: Timing a Job Previous: Timing a Job in
Zdzislaw Meglicki