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:
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:
Subroutine system_clock is somewhat easier to use. It takes 3 optional arguments:
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 xlfutilityThe 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
#endif
! 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_ ...'
#endif
! 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)
#endif
write (6, '(8x, 1a, 1f16.6)') 'begin (date_and_time): ', &
start_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): ', &
etime_struct_0%usrtime
write (6, '(8x, 1a, 1f16.6)') 'begin (etime_%systime): ', &
etime_struct_0%systime
#endif
! Sleep for 5 seconds
#ifdef XLF
write (6, '(16x, 1a)') 'sleep for 5 seconds ... '
call sleep_ (5)
#endif
! 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)
#endif
write (6, '(8x, 1a, 1f16.6)') 'end (date_and_time): ', &
finish_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): ', &
etime_struct_1%usrtime
write (6, '(8x, 1a, 1f16.6)') 'end (etime_%systime): ', &
etime_struct_1%systime
#endif
! 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
#endif
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
DEFINES = -DXLF
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
clean:
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.