Результаты параллельной программы с вложенными циклами отличаются от последовательной программы

Я хотел бы использовать OpenMP для этого однопоточного кода:

PROGRAM SINGLE
  INTEGER, DIMENSION(30000)::SUMGRM
  INTEGER, DIMENSION(90000)::GRI,H
  REAL*8::HSTEP1X,HSTEP2X
  REAL*8::TIME1,TIME2

!Just intiial value
  DO I=1, 30000
     SUMGRM(I)=I*3        
  END DO

  DO I=1, 90000
     GRI(I)=I
     H(I)=0.5*I/10000    
  END DO

!Computing computer's running time (start) : for serial programming
 CALL CPU_TIME(TIME1)

 DO K=1, 50000
    DO I=2, 30000
       HSTEP1X=0.0    
         DO J=SUMGRM(I-1)+1, SUMGRM(I)-1
            HSTEP2X=H(GRI(J))/0.99
            HSTEP1X=HSTEP1X+HSTEP2X       
         END DO
       HSTEP2X=H(GRI(SUMGRM(I)))/0.99
       HSTEP1X=HSTEP1X+HSTEP2X         
    END DO
 END DO

  PRINT *, 'Results  =', HSTEP1X
  PRINT *, '   '

!Computing computer's running time (finish) : for serial programming
 CALL CPU_TIME(TIME2)
 PRINT *, 'Elapsed real time = ', TIME2-TIME1, 'second(s)'
END PROGRAM SINGLE

Как видите, основная проблема заключается в самой внутренней боковой петле (J), который также является функцией большинства внешних боковых петель (I). Я попытался распараллелить эту программу следующим образом:

PROGRAM PARALLEL
  INTEGER, DIMENSION(30000)::SUMGRM
  INTEGER, DIMENSION(90000)::GRI,H
  REAL*8::HSTEP1X,HSTEP2X
  REAL*8::TIME1,TIME2,OMP_GET_WTIME
  INTEGER::Q2,P2

!Just intiial value
  DO I=1, 30000
     SUMGRM(I)=I*3        
  END DO

  DO I=1, 90000
     GRI(I)=I
     H(I)=0.5*I/10000  
  END DO

!Computing computer's running time (start) : for parallel programming
 TIME1= OMP_GET_WTIME()

 DO K=1, 50000
 !$OMP PARALLEL DO PRIVATE (HSTEP1X,Q2,P2)
    DO I=2, 30000
       HSTEP1X=0.0
       Q2=SUMGRM(I-1)+1
       P2=SUMGRM(I)-1
         DO J=Q2, P2
            HSTEP2X=H(GRI(J))/0.99
            HSTEP1X=HSTEP1X+HSTEP2X       
         END DO
       HSTEP2X=H(GRI(SUMGRM(I)))/0.99
       HSTEP1X=HSTEP1X+HSTEP2X     
    END DO
 !$OMP END PARALLEL DO
 END DO

 PRINT *, 'Results  =', HSTEP1X
 PRINT *, '   '

!Computing computer's running time (finish) : for parallel programming
 TIME2= OMP_GET_WTIME()
 PRINT *, 'Elapsed real time = ', TIME2-TIME1, 'second(s)'
END PROGRAM PARALLEL

я использую gfortran with -O3 -fopenmp а затем экспортировать OMP_NUM_THREADS=... Параллельная программа работает быстрее, но результат отличается от кода с одним потоком. По серийной программе я получил 12.1212 (который является правильным) и параллельно я получил 0.000 (должно быть что-то не так).

Что я сделал не так?

2 ответа

Решение

Во-первых, мы можем заметить, что по умолчанию вы, вероятно, обнаружите, что оба j а также hstep2x будут разделены между потоками. Я не думаю, что это действительно то, что вам нужно, поскольку это приведет к очень странному поведению, когда несколько потоков используют один и тот же индекс итерации, но пытаются перебрать разные диапазоны.

Далее отметим, что ваш серийный код на самом деле просто печатает результат для i=30000 итерация как значение hstep1x сбрасывается в 0 в начале каждой итерации. Таким образом, чтобы получить "правильный" ответ в коде openmp, мы могли бы просто сосредоточиться на воспроизведении финальной итерации - это полностью сводит на нет смысл использования openmp здесь, я думаю. Я предполагаю, что это просто простой случай, который вы пытаетесь использовать, чтобы представить свою реальную проблему - я думаю, что вы, возможно, упустили некоторые из реальных проблем при создании этого.

Тем не менее приведенный ниже код выдает "правильный" ответ на моей машине. Я не уверен, насколько это гибко, но это работает здесь.

PROGRAM PARALLEL
  INTEGER, DIMENSION(30000)::SUMGRM
  INTEGER, DIMENSION(90000)::GRI,H
  REAL*8::HSTEP1X,HSTEP2X
  REAL*8::TIME1,TIME2,OMP_GET_WTIME
  INTEGER::Q2,P2

!Just intiial value                                                                                                                                                                                                  
  DO I=1, 30000
     SUMGRM(I)=I*3
  END DO

  DO I=1, 90000
     GRI(I)=I
     H(I)=0.5*I/10000
  END DO

!Computing computer's running time (start) : for parallel programming                                                                                                                                                
 TIME1= OMP_GET_WTIME()

 DO K=1, 50000
!$OMP PARALLEL DO PRIVATE (Q2,P2,J,HSTEP2X) DEFAULT(SHARED) LASTPRIVATE(HSTEP1X)                                                                                                                                     
    DO I=2, 30000
       HSTEP1X=0.0
       Q2= SUMGRM(I-1)+1
       P2= SUMGRM(I)-1
         DO J=Q2,P2
            HSTEP2X=H(GRI(J))/0.99
            HSTEP1X=HSTEP1X+HSTEP2X
         END DO
       HSTEP2X=H(GRI(SUMGRM(I)))/0.99
       HSTEP1X=HSTEP1X+HSTEP2X
    END DO
!$OMP END PARALLEL DO                                                                                                                                                                                                
END DO

 PRINT *, 'Results  =', HSTEP1X
 PRINT *, '   '

!Computing computer's running time (finish) : for parallel programming                                                                                                                                               
 TIME2= OMP_GET_WTIME()
 PRINT *, 'Elapsed real time = ', TIME2-TIME1, 'second(s)'
END PROGRAM PARALLEL

Я сделал три вещи здесь:

  1. Удостовериться j а также hstep2x являются частными для каждой темы.
  2. Явно объявлено поведение по умолчанию для совместного использования (здесь не нужно, но не важно).
  3. Указано что hstep1x является lastprivate, Это означает, что после выхода из параллельной области значение hstep1x это то, что взято из потока, который выполнил последнюю итерацию. (подробности см. здесь)

Вы пытались использовать

!$OMP PARALLEL DO DEFAULT(PRIVATE) REDUCTION(+:HSTEP1X)                                                                                                                                     
Другие вопросы по тегам