Reputation: 33
I am recently working on a fortran90 program which calculate the time needed and result of some mathematics calculation. Here is the code:
program loops
use omp_lib
implicit none
integer, parameter :: N=729
integer, parameter :: reps=1000
real(kind=8), allocatable :: a(:,:), b(:,:), c(:)
integer :: jmax(N)
real(kind=8) :: start1,start2,end1,end2
integer :: r
allocate(a(N,N), b(N,N), c(N))
call init1()
start1 = omp_get_wtime()
do r = 1,reps
call loop1()
end do
end1 = omp_get_wtime()
call valid1();
print *, "Total time for ",reps," reps of loop 1 = ", end1-start1
call init2()
start2 = omp_get_wtime()
do r = 1,reps
call loop2()
end do
end2 = omp_get_wtime()
call valid2();
print *, "Total time for ",reps," reps of loop 2 = ", end2-start2
contains
subroutine init1()
implicit none
integer :: i,j
do i = 1,N
do j = 1,N
a(j,i) = 0.0
b(j,i) = 3.142*(i+j)
end do
end do
end subroutine init1
subroutine init2()
implicit none
integer :: i,j,expr
do i = 1,N
expr = mod(i,3*(i/30)+1)
if (expr == 0) then
jmax(i) = N
else
jmax(i) = 1
end if
c(i) = 0.0
end do
do i = 1,N
do j = 1,N
b(j,i) = dble(i*j+1)/dble(N*N)
end do
end do
end subroutine init2
subroutine loop1()
implicit none
integer :: i,j
!$OMP PARALLEL DO DEFAULT(NONE), PRIVATE(i,j), SHARED(a,b), SCHEDULE(type,chunksize)
do i = 1,N
do j = N,i,-1
a(j,i) = a(j,i) + cos(b(j,i))
end do
end do
!$OMP END PARALLEL DO
end subroutine loop1
subroutine loop2()
implicit none
integer :: i,j,k
real (kind=8) :: rN2
rN2 = 1.0 / dble (N*N)
!$OMP PARALLEL DO DEFAULT(NONE), PRIVATE(i,j,k), SHARED(rN2,c,b,jmax), SCHEDULE(type,chunksize)
do i = 1,N
do j = 1, jmax(i)
do k = 1,j
c(i) = c(i) + k * log(b(j,i)) *rN2
end do
end do
end do
!$OMP END PARALLEL DO
end subroutine loop2
subroutine valid1()
implicit none
integer :: i,j
real (kind=8) :: suma
suma= 0.0
do i = 1,N
do j = 1,N
suma = suma + a(j,i)
end do
end do
print *, "Loop 1 check: Sum of a is ", suma
end subroutine valid1
subroutine valid2()
implicit none
integer i
real (kind=8) sumc
sumc= 0.0
do i = 1,N
sumc = sumc + c(i)
end do
print *, "Loop 2 check: Sum of c is ", sumc
end subroutine valid2
end program loops
In the line !$OMP PARALLEL DO DEFAULT(NONE), PRIVATE(i,j), SHARED(a,b), SCHEDULE(type,chunksize)
and !$OMP PARALLEL DO DEFAULT(NONE), PRIVATE(i,j,k), SHARED(rN2,c,b,jmax), SCHEDULE(type,chunksize)
.
As I want to perform the task of different schedule case to see the different results, so I need to change this part SCHEDULE(type,chunksize)
, with different schedule type and different chunksize. For example, in this case, the schedule type is static and chunksize is 1.
Say if I have type of (static, a, b, c) and chunksize (1,2,3,4,5,6,7). As I am new to fortran so I wonder is it possible to compile and run the code for all case in once without the fact that I have to change the parameters manually everytime, i.e it compiles and runs to give the result of first case e.g (static,1), it then compiles and runs the file again but with the parameters changed automatically that gives another result. For instance, (static,2)...(b,4) etc.
I heard that we can create a script file to perform such task, but I not am sure what exactly I need to do for this.
Thank you so much.
Upvotes: 0
Views: 252
Reputation: 1484
You may want to investigate the use of the preprocessor. I'm speaking from experience with gfortran, but I believe this applies (almost) all other compilers as well even though it is outside the scope of the Fortran standard.
If you name your source file with a capital F in the suffix, i.e. file.F, file.F90, file.F95 etc, your file will be preprocessed with the C preprocessor before being compiled. That may sound complicated, but cutting this down to what you need, this means that if you compile your code with a command like
$ gfortran -DCHUNK_SIZE=1 mySource.F90
then all occurrences of CHUNK_SIZE
(with qualifiers which are not essential to your problem) will be replaced by 1
. More technically, CHUNK_SIZE
becomes a macro defined to expand to 1
. So if you replace SCHEDULE(type,chunksize)
with SCHEDULE(type,CHUNK_SIZE)
in your source file, you can repeatedly invoke the compiler with different values, -DCHUNK_SIZE=1
, -DCHUNK_SIZE=2
etc, and get the result that you described. The same can be done for type
.
Now you may want to change the function names accordingly as well. One way would be to add a few preprocessor statements near the top of your file declaring a few macros, namely
#ifdef __GFORTRAN__
#define PASTE2(a,b) a/**/b
#define FUNC_NAME_WITH_CHUNK_SIZE(fn) PASTE2(PASTE2(fn,_),CHUNK_SIZE)
#else
#define FUNC_NAME_WITH_CHUNK_SIZE(fn) fn ## _ ## CHUNK_SIZE
#endif
#define LOOP1 FUNC_NAME_WITH_CHUNK_SIZE(loop1)
#define LOOP2 FUNC_NAME_WITH_CHUNK_SIZE(loop2)
and replace loop1
with LOOP1
etc. You could do this from the command line as before, but since these rules are not supposed to change between compilations, it makes sense to keep these in the source file. I think the only part that is not self-explanatory is the use of ##
and /**/
between #ifdef
and #endif
. This is how one does string concatenation with the preprocessor, and because gfortran uses the way C preprocessors did it before the language was standardized, it gets exceptional treatment, see e.g. this answer for some info on these operators. The purpose of this operation is to replace LOOP1
with loop1_<CHUNK_SIZE>
, where <CHUNK_SIZE>
is filled in from the command line. Feel free to follow any other conventions for naming these functions.
If you want to call these functions from another translation unit, you will have to process the function names in the same way, of course. In order to make your life easier, you may want to research the #include
statement. Detailing this would take us too far here, but the idea is that you put all your includes into a file (conventionally named <something>.inc
in the Fortran-world with <something>
replaced that makes sense to you) and use #include "<something>.inc
in all source files to obtain the same macro definitions.
Upvotes: 2