我最近正在研究一个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。
如果我具有(静态,a,b,c)类型和块大小(1、2、3、4、5、6、7),请说。由于我是fortran的新手,所以我想知道是否可以针对所有情况一次编译并运行代码,而不必每次都必须手动更改参数,即它可以编译并运行以给出第一种情况的结果,例如( static,1),然后再次编译并运行文件,但参数会自动更改,从而产生另一个结果。例如(static,2)...(b,4)等
我听说我们可以创建一个脚本文件来执行这样的任务,但是我不确定我到底需要做什么。
非常感谢您。
答案 0 :(得分:2)
您可能要研究预处理器的使用。我从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预处理器在语言标准化之前进行的方式,因此得到了特殊的待遇,请参见例如。 this answer,以获取有关这些运算符的一些信息。此操作的目的是将LOOP1
替换为loop1_<CHUNK_SIZE>
,其中<CHUNK_SIZE>
是从命令行填写的。随意遵循其他约定命名这些功能。
如果要从另一个翻译单元调用这些函数,则当然必须以相同的方式处理这些函数名。为了使您的生活更轻松,您可能需要研究#include
语句。详细说明这一点会使我们走得太远了,但是我们的想法是将所有包含内容放入一个文件(在Fortran世界中通常命名为<something>.inc
,替换为<something>
对您来说很有意义)并使用在所有源文件中#include "<something>.inc
以获得相同的宏定义。