почему вызов N раз последовательной подпрограммы быстрее, чем вызов N параллельной версии одной подпрограммы - PullRequest
0 голосов
/ 22 июня 2019

У меня есть последовательная версия подпрограммы "serial" и параллельная версия e той же подпрограммы "paral". Я использую openmp. Когда я вызываю серийный номер, я измеряю 0,475 секунды времени настенных часов. Когда я вызываю параллельную, я измеряю 0,09309 секунд времени настенных часов. Общее ускорение = 5Х, отличный результат. Но когда я пытаюсь:

    do jjj=1,10
         call serial
    end do

Я измерил 1,398 секунды времени настенных часов, когда я ожидал 0,475 * 10 = 4,75 секунды. Когда я пробую ту же петлю, но с «call paral», я измеряю 1,08 секунды. Общее ускорение версий цикла = 1.398 / 1.08 = 1.3 X. Плохие результаты. Когда последовательная подпрограмма вызывается много раз, она слишком хорошо масштабируется. Когда параллельная подпрограмма вызывается много раз, она масштабируется линейно (как и ожидалось). Некоторые предложения по этому поводу? Я хочу сохранить 5-кратное ускорение даже с версией петли. Я использую гфортран. Спасибо

EDIT: и вот код, который я использую для измерения производительности, пожалуйста, скомпилируйте с: gfortran -fopenmp -fdefault-real-8 -fdefault-integer-8 -fdefault-double-8 -mcmodel = большой код.f

  PARAMETER(KMM1=80, JM=500 , IMM1=80  )
  COMMON/VEC/ AQX(IMM1,JM,KMM1),AQR(IMM1,JM,KMM1), 
 &           ROVX(IMM1,JM,KMM1),RR(IMM1,JM,KMM1),           
 &           FX(IMM1,JM,KMM1),XX(IMM1,JM,KMM1),
 &           SE(IMM1,JM,KMM1)


  COMMON/VRB/ KMMM,JMMM,IMMM
  include 'omp_lib.h'
  DOUBLE PRECISION SEC,SEC1,SEC11,SEC22


  KMMM=80
  JMMM=500
  IMMM=80

  CALL OMP_SET_NUM_THREADS(4)

  SEC1 = omp_get_wtime()


  do JJJJ=1,10
      CALL SERIAL
  END DO

  SEC = omp_get_wtime() - SEC1
  WRITE(6,*) '#CPU SERIAL=', SEC, 'SECONDS.'

  SEC11 = omp_get_wtime()

  DO JJJJ=1,10
      CALL PARAL
  END DO

  SEC22 = omp_get_wtime() - SEC11
  WRITE(6,*) '#CPU PARALLEL=', SEC22, 'SECONDS.'
  WRITE(6,*) 'SPEEDUP = ', SEC/SEC22



  STOP
  END


  SUBROUTINE SERIAL
  PARAMETER(KMM1=80, JM=500 , IMM1=80  )
  COMMON/VEC/ AQX(IMM1,JM,KMM1),AQR(IMM1,JM,KMM1), 
 &           RX(IMM1,JM,KMM1),RR(IMM1,JM,KMM1),           
 &           FX(IMM1,JM,KMM1),XX(IMM1,JM,KMM1),
 &           SE(IMM1,JM,KMM1)


  COMMON/VRB/ KMMM,JMMM,IMMM


  DO K=1,KMMM
  DO J=1,JMMM  
  DO I=1,IMMM
      AX = RX(I,J,K)+RX(I,J,K+1)+RX(I+1,J,K)+RX(I+1,J,K+1)
      AR = RR(I,J,K)+RR(I,J,K+1)+RR(I+1,J,K)+RR(I+1,J,K+1)
      FX(I,J,K)   = 0.25*(AX*AQX(I,J,K) + AR*AQR(I,J,K))
      SE(I,J,K ) = 0.0
      XX(I,J,K)   = FX(I,J,K)
  END DO
  END DO
  END DO


  RETURN
  END


  SUBROUTINE PARAL

  include 'omp_lib.h'

  PARAMETER(KMM1=80, JM=500, IMM1=80  )
  COMMON/VEC/ AQX(IMM1,JM,KMM1),AQR(IMM1,JM,KMM1), 
 &           RX(IMM1,JM,KMM1),RR(IMM1,JM,KMM1),           
 &           FX(IMM1,JM,KMM1),XX(IMM1,JM,KMM1),
 &           SE(IMM1,JM,KMM1)

  COMMON/VRB/ KMMM,JMMM,IMMM


!$OMP PARALLEL DO DEFAULT(NONE) PRIVATE(AX,AR) 
!$OMP+ SHARED(RX,RR,AQX,AQR,SE,FX,XX,JMMM,IMMM,KMMM)

  DO K=1,KMMM
  DO J=1,JMMM  
  DO I=1,IMMM
      AX = RX(I,J,K)+RX(I,J,K+1)+RX(I+1,J,K)+RX(I+1,J,K+1)
      AR = RR(I,J,K)+RR(I,J,K+1)+RR(I+1,J,K)+RR(I+1,J,K+1)
      FX(I,J,K)   = 0.25*(AX*AQX(I,J,K) + AR*AQR(I,J,K))
      SE(I,J,K ) = 0.0
      XX(I,J,K)   = FX(I,J,K)
  END DO
  END DO
  END DO
!$OMP END PARALLEL DO


  RETURN
  END

EDIT2: я провел несколько тестов, и похоже, что ускорение, наблюдаемое только при одном вызове, было вызвано использованием интеллектуального кэша. Если вы переключаете последовательную синхронизацию с параллельной синхронизацией, вы можете проверить (только с вызовом), что выгода от использования кэша. Реальное ускорение измеряется при многократном вызове подпрограмм.

...