我正在尝试并行化包含大量内存读数的do循环。该代码用fortran 77编写。
我根本没有提速。这是并行化的代码。它给出正确的结果。在代码A,B,C,D中,是在常见部分(如COMMON / ARR / A(IM,JM,KM),ETC ...)中定义的数组,并且循环是多次调用的子例程的一部分。>
!$OMP PARALLEL DO DEFAULT(NONE) PRIVATE(AVGRVX,AVGRVR)
!$OMP+ SHARED(A,B,AQX,AQR,SOURCEP,C,D,JMMM,IMMM)
DO K=1,KMMM
DO J=1,JMMM
DO I=1,IMMM
AVGRVX = A(I,J,K)+A(I,J,K+1)+A(I+1,J,K)+A(I+1,J,K+1)
AVGRVR = B(I,J,K)+B(I,J,K+1)+B(I+1,J,K)+B(I+1,J,K+1)
C(I,J,K) = 0.25*(AVGRVX*AQX(I,J,K) + AVGRVR*AQR(I,J,K))
SOURCEP(I,J,K ) = 0.0
D(I,J,K) = C(I,J,K)
END DO
END DO
END DO
!$OMP END PARALLEL DO
实际加速为0.98X。并行版本的运行速度比串行版本慢。我认为这是因为每个线程都没有执行大量计算,而是一直访问所有内存。这不是因为JMMM,KMMM等的尺寸。如果它们变大,则代码将变得更慢。我正在寻找有关如何改进代码的建议。例如,可以在fortran90 / 95中转换代码并使用可分配的数组而不是普通的数组带来一些好处吗?谢谢
编辑:这是我用于测试循环的代码,对于gfortran,请使用gfortran -fopenmp -fdefault-real-8 -fdefault-integer-8 -fdefault-double-8 -mcmodel = large -O3此代码进行编译.f:
PARAMETER(KMM1=70, JM=2500 , IMM1=70 )
COMMON/VEC/ AQX(IMM1,JM,KMM1),AQR(IMM1,JM,KMM1),
& A(IMM1,JM,KMM1),B(IMM1,JM,KMM1),
& C(IMM1,JM,KMM1),D(IMM1,JM,KMM1),
& SOURCE(IMM1,JM,KMM1)
COMMON/PARVEC/ CP(IMM1,JM,KMM1),SOURCEP(IMM1,JM,KMM1),
& DP(IMM1,JM,KMM1)
COMMON/VRB/ KMMM,JMMM,IMMM
include 'omp_lib.h'
DOUBLE PRECISION SEC,SEC1
KMMM=60
JMMM=150
IMMM=60
CALL OMP_SET_NUM_THREADS(4)
CALL RANDOM_SEED()
DO K=1,KMM1
DO J=1,JM
DO I=1,IMM1
CALL RANDOM_NUMBER(A(I,J,K))
CALL RANDOM_NUMBER(B(I,J,K))
CALL RANDOM_NUMBER(AQX(I,J,K))
CALL RANDOM_NUMBER(AQR(I,J,K))
CALL RANDOM_NUMBER(SOURCE(I,J,K))
END DO
END DO
END DO
SEC1 = omp_get_wtime()
CALL SERIAL
SEC = omp_get_wtime() - SEC1
WRITE(6,*) '#CPU SERIAL=', SEC, 'SECONDS.'
SEC1 = omp_get_wtime()
CALL PARAL
SEC = omp_get_wtime() - SEC1
WRITE(6,*) '#CPU PARALLEL=', SEC, 'SECONDS.'
DO K=1,KMM1
DO J=1,JM
DO I=1,IMM1
IF(CP(I,J,K).NE.C(I,J,K)) THEN
WRITE(6,*) 'ERROR'
END IF
IF(DP(I,J,K).NE.D(I,J,K)) THEN
WRITE(6,*) 'ERROR'
END IF
END DO
END DO
END DO
STOP
END
SUBROUTINE SERIAL
PARAMETER(KMM1=70, JM=2500 , IMM1=70 )
COMMON/VEC/ AQX(IMM1,JM,KMM1),AQR(IMM1,JM,KMM1),
& A(IMM1,JM,KMM1),B(IMM1,JM,KMM1),
& C(IMM1,JM,KMM1),D(IMM1,JM,KMM1),
& SOURCE(IMM1,JM,KMM1)
COMMON/PARVEC/ CP(IMM1,JM,KMM1),SOURCEP(IMM1,JM,KMM1),
& DP(IMM1,JM,KMM1)
COMMON/VRB/ KMMM,JMMM,IMMM
DO K=1,KMMM
DO J=1,JMMM
DO I=1,IMMM
AVGRVX = A(I,J,K)+A(I,J,K+1)+A(I+1,J,K)+A(I+1,J,K+1)
AVGRVR = B(I,J,K)+B(I,J,K+1)+B(I+1,J,K)+B(I+1,J,K+1)
C(I,J,K) = 0.25*(AVGRVX*AQX(I,J,K) + AVGRVR*AQR(I,J,K))
SOURCE(I,J,K ) = 0.0
D(I,J,K) = C(I,J,K)
END DO
END DO
END DO
RETURN
END
SUBROUTINE PARAL
PARAMETER(KMM1=70, JM=2500 , IMM1=70 )
COMMON/VEC/ AQX(IMM1,JM,KMM1),AQR(IMM1,JM,KMM1),
& A(IMM1,JM,KMM1),B(IMM1,JM,KMM1),
& C(IMM1,JM,KMM1),D(IMM1,JM,KMM1),
& SOURCE(IMM1,JM,KMM1)
COMMON/PARVEC/ CP(IMM1,JM,KMM1),SOURCEP(IMM1,JM,KMM1),
& DP(IMM1,JM,KMM1)
COMMON/VRB/ KMMM,JMMM,IMMM
!$OMP PARALLEL DO DEFAULT(NONE) PRIVATE(AVGRVX,AVGRVR)
!$OMP+ SHARED(A,B,AQX,AQR,SOURCEP,CP,DP,JMMM,IMMM)
DO K=1,KMMM
DO J=1,JMMM
DO I=1,IMMM
AVGRVX = A(I,J,K)+A(I,J,K+1)+A(I+1,J,K)+A(I+1,J,K+1)
AVGRVR = B(I,J,K)+B(I,J,K+1)+B(I+1,J,K)+B(I+1,J,K+1)
CP(I,J,K) = 0.25*(AVGRVX*AQX(I,J,K) + AVGRVR*AQR(I,J,K))
SOURCEP(I,J,K ) = 0.0
DP(I,J,K) = CP(I,J,K)
END DO
END DO
END DO
!$OMP END PARALLEL DO
RETURN
END