如何使用openmp fortrann77解决内存带宽问题

时间:2019-06-06 14:41:26

标签: fortran openmp

我正在尝试并行化包含大量内存读数的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

0 个答案:

没有答案