Я пытаюсь распараллелить цикл do, который содержит много чтений из памяти.Код написан на фортране 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,98 X. Параллельная версия работает медленнее, чем последовательная.Я думаю, это потому, что каждый поток не выполняет много вычислений, а просто постоянно обращается к памяти.Это не из-за размеров JMMM, KMMM и т. Д. Если они становятся больше, тогда код становится еще медленнее.Я ищу предложения о том, как улучшить код.Может ли перевод кода, например, в fortran90 / 95 и использование выделяемых вместо общих массивов, принести некоторые преимущества?Спасибо
РЕДАКТИРОВАТЬ: Вот код, который я использую для проверки цикла, с gfortran, пожалуйста, скомпилируйте с gfortran -fopenmp -fdefault-real-8 -fdefault-integer-8 -fdefault-double-8 -mcmodel= большой -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