2016-07-20 8 views
0

Я хотел бы использовать 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 (должно быть что-то не так).

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

+0

Используйте тег PTAG: Фортран] для всех Фортрана вопросов. Добавьте тег версии, где необходимо различать. Обратите внимание на количество людей, подписавшихся на [tag: fortran] и [tag: fortran95], и вы хотите, чтобы больше людей увидели ваш вопрос, не так ли? –

+3

Почти верно сказать, что параллельные программы всегда приносят разные результаты своим последователям-кузенам. Конкретно о том, как результаты разные, из различий многое можно узнать. –

+0

Попробуйте использовать заголовки, определяющие вашу проблему, а не только тему. Тема довольно хорошо определена тегами. –

ответ

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 будет взято из потока, который выполнил последнюю итерацию. (см. here).
+0

Да, вы абсолютно правдивы. Это не реальный случай, так как абсолютно фантастический случай и конечно [DO I = 2, 30000] абсолютно не требуется. Я просто добавил его, чтобы понять, как работает параллельный код, а также [DO K = 1, 50000] не требуется, чтобы увидеть представителя разности процессорных процессоров. Да, я забыл о включении [HSTEP1X] в [LASTPRIVATE]. Кстати, код также работает на моей машине. Ваше объяснение было замечательным. –

+0

О да, я забыл прокомментировать, что [J] может быть опционально определен как [ЧАСТЬ], поскольку он находится внутри цикла, но оба значения [Q2] и [P2] должны храниться как [ЧАСТ.]. –

0

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

!$OMP PARALLEL DO DEFAULT(PRIVATE) REDUCTION(+:HSTEP1X)                                  
+0

Я думаю, что это не сработает, так как главная проблема здесь в том, что [HSTEP1X] имеет свое значение (ноль) перед циклом [DO J = Q2, P2]. Итак, если мы используем [DEFAULT (PRIVATE)], значение [HSTEP1X] останется неизменным как ноль. –

+0

Возможно, вы правы bob.bob.bob, что gfortran кажется намного сложнее, чем ifort. Я установил его сегодня и борюсь за различия. В любом случае, давайте проигнорируем ПО УМОЛЧАНИЮ ... СОКРАЩЕНИЕ похоже, что это необходимо здесь? – Holmz

+0

Не могли бы вы указать, что «намного сложнее» вы имеете в виду? Поскольку в моем опыте я использовал Gfortran и никогда не испытывал серьезных проблем, даже когда я использую новый Intel fortran 16 на прошлой неделе, я получил сообщение, так как оно не поддерживает файлы с форматом .F95 и более поздними версиями. И, наконец, я должен изменить формат на .F90 (но со старым Intel Fortran 13, так как новый еще не работал), а затем он работал ... Да, вы можете абсолютно игнорировать [DEFAULT (SHARED)] в этом случае, поскольку это не имеет значения. Это моя привычка, которая предпочитает писать ее полностью. –