Компиляция файлов fortran90 с разными параметрами каждый раз

Недавно я работаю над программой fortran90, которая рассчитывает необходимое время и результат некоторых математических расчетов. Вот код:

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

В соответствии !$OMP PARALLEL DO DEFAULT(NONE), PRIVATE(i,j), SHARED(a,b), SCHEDULE(type,chunksize) а также !$OMP PARALLEL DO DEFAULT(NONE), PRIVATE(i,j,k), SHARED(rN2,c,b,jmax), SCHEDULE(type,chunksize),

Поскольку я хочу выполнить задачу в другом расписании, чтобы увидеть разные результаты, мне нужно изменить эту часть SCHEDULE(type,chunksize), с другим типом расписания и разными размерами. Например, в этом случае тип расписания является статическим, а размер фрагмента равен 1.

Скажите, если у меня есть тип (статические, а, б, в) и размер (1,2,3,4,5,6,7). Поскольку я новичок в Фортране, поэтому мне интересно, можно ли скомпилировать и запустить код для всех случаев за один раз, не требуя того, чтобы мне приходилось каждый раз менять параметры вручную, то есть он компилируется и запускается для получения результата первого случая, например (static,1), затем он снова компилирует и запускает файл, но параметры автоматически изменяются, что дает другой результат. Например, (статический,2)...(б,4) и т. Д.

Я слышал, что мы можем создать файл сценария для выполнения такой задачи, но я не уверен, что именно мне нужно для этого сделать.

Огромное спасибо.

1 ответ

Вы можете захотеть исследовать использование препроцессора. Я говорю из опыта работы с gfortran, но я считаю, что это применимо (почти) ко всем другим компиляторам, даже если это выходит за рамки стандарта Fortran.

Если вы присвоите исходному файлу заглавную букву F в суффиксе, то есть file.F, file.F90, file.F95 и т. Д., Ваш файл будет предварительно обработан препроцессором C перед компиляцией. Это может показаться сложным, но сокращение этого до того, что вам нужно, это означает, что если вы скомпилируете свой код с помощью команды вроде

$ gfortran -DCHUNK_SIZE=1 mySource.F90

тогда все случаи CHUNK_SIZE (с квалификаторами, которые не важны для вашей проблемы) будут заменены на 1, Более технически, CHUNK_SIZE становится макросом, определенным для расширения до 1, Так что если вы замените SCHEDULE(type,chunksize) с SCHEDULE(type,CHUNK_SIZE) в вашем исходном файле вы можете повторно вызывать компилятор с разными значениями, -DCHUNK_SIZE=1, -DCHUNK_SIZE=2 и т.д., и получите результат, который вы описали. То же самое можно сделать для type,

Теперь вы можете изменить имена функций соответственно. Одним из способов было бы добавить несколько инструкций препроцессора в верхней части вашего файла, объявляя несколько макросов, а именно

#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)

и заменить loop1 с LOOP1 и т. д. Вы можете сделать это из командной строки, как и раньше, но поскольку эти правила не должны изменяться между компиляциями, имеет смысл сохранить их в исходном файле. Я думаю, что единственная часть, которая не говорит само за себя, это использование ## а также /**/ между #ifdef а также #endif, Это то, как можно выполнить конкатенацию строк с препроцессором, и поскольку gfortran использует способ, которым препроцессоры C делали это до стандартизации языка, он получает исключительную обработку, см., Например, этот ответ для некоторой информации об этих операторах. Цель этой операции - заменить LOOP1 с loop1_<CHUNK_SIZE>, где <CHUNK_SIZE> заполняется из командной строки. Не стесняйтесь следовать любым другим соглашениям для именования этих функций.

Если вы хотите вызвать эти функции из другого модуля перевода, вам, конечно, придется обрабатывать имена функций таким же образом. Чтобы сделать вашу жизнь проще, вы можете исследовать #include заявление. Детализация этого приведет нас слишком далеко, но идея состоит в том, что вы помещаете все свои включения в файл (условно названный <something>.inc в мире Фортран с <something> заменить, что имеет смысл для вас) и использовать #include "<something>.inc во всех исходных файлах для получения одинаковых определений макросов.

Другие вопросы по тегам