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
system_clock, which return elapsed wall-clock
time in various formats.
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_arrayis our array of integers. The returned values will have the following ordering:
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
counts per second up to
count_max, and then resets itself to zero and resumes the counting. But unlike
function measures wall-clock time, not the CPU time. As you will see from the following example, under AIX
system_clock resets every day at midnight. But this particular behaviour is not specified in F90
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
-P ensures that. The importance of option
-C will become clearer in our next Fortran-90
etime_ is defined in the
xlfutility module, which must be included with the
use xlfutilityThe function takes a structure of type
(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
systimeslots 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_timeOn 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_clockis roughly the same as time returned by procedure
date_and_time, which means that
system_clockmust be reset at midnight, as I have already remarked above.