Плохое масштабирование и ошибка сегментации в коде Fortran OpenMP - PullRequest
2 голосов
/ 11 марта 2019

У меня возникли проблемы при выполнении программы с параллельным выполнением.Вот тестовый код.

module test
use, intrinsic :: iso_fortran_env, only: dp => real64
implicit none
contains

subroutine Addition(x,y,s)
    real(dp),intent(in) :: x,y
    real(dp), intent(out) :: s
    s = x+y
end subroutine Addition

function linspace(length,xi,xf) result (vec)
! function to create an equally spaced vector given a begin and end point
    real(dp),intent(in) :: xi,xf
    integer, intent(in) :: length
    real(dp),dimension(1:length) :: vec
    integer ::i
    real(dp) :: increment

    increment = (xf-xi)/(real(length)-1)
    vec(1) = xi
    do i = 2,length
        vec(i) = vec(i-1) + increment
    end do
end function linspace
end module test

program paralleltest
use, intrinsic :: iso_fortran_env, only: dp => real64
use test
use :: omp_lib
implicit none
integer, parameter :: length = 1000
real(dp),dimension(length) :: x,y
real(dp) :: s
integer:: i,j
integer :: num_threads = 8
real(dp),dimension(length,length) :: SMatrix

 x = linspace(length,.0d0,1.0d0)
 y = linspace(length,2.0d0,3.0d0)

!$ call omp_set_num_threads(num_threads)
!$OMP PARALLEL DO
do i=1,size(x)
    do j = 1,size(y)
    call Addition(x(i),y(j),s)
    SMatrix(i,j) = s
    end do
end do
!$OMP END PARALLEL DO

open(unit=1,file ='Add6.dat')
do i= 1,size(x)
    do j= 1,size(y)
        write(1,*) x(i),";",y(j),";",SMatrix(i,j)
    end do
end do
close(unit=1)
end program paralleltest

Я запускаю программу следующим образом gfortran-8 -fopenmp paralleltest.f03 -o pt.out -mcmodel=medium, а затем export OMP_NUM_THREADS=8 Этот простой код ставит мне по крайней мере два больших вопроса о параллельной работе.Во-первых, если я запускаю с length = 1100 или выше, у меня появляется сообщение об ошибке Segmentation fault (core dump), но при меньших значениях оно запускается без проблем.Второе о времени, которое требуется.Когда я запускаю его с length = 1000 (запускаем с time ./pt.out), это занимает 1,732s, но если я запускаю его последовательно (без вызова библиотеки -fopenmp и с taskset -c 4 time./pt.out), это занимает 1,714s.Я предполагаю, что разница между обоими способами возникает в более длинном и более сложном коде, где параллель более полезна.На самом деле, когда я попробовал это с более сложными вычислениями, работающими параллельно с восемью потоками, время было уменьшено вдвое, по сравнению с последовательным, а не восьмым, как я ожидал.В связи с этим мои вопросы: всегда ли доступна какая-либо оптимизация или она зависит от кода?и во-вторых, есть ли дружественный способ контролировать, какой поток выполняет итерацию?Это первый запуск первой итерации length/8 и т. Д., Аналогичный выполнению нескольких taskset с различным кодом, где в каждой есть итерация, которую я хочу.

1 Ответ

3 голосов
/ 11 марта 2019

Как я уже говорил, ошибка сегментации была рассмотрена в другом месте Почему в этом коде openmp происходит ошибка сегментации? , я бы использовал выделяемый массив, но вы также можете установить размер стека с помощью ulimit -s.

Что касается времени, то почти все время выполнения тратится на запись массива во внешний файл.

Но даже если вы удалите это и измерите время, проведенное только в параллельном разделе, с помощью omp_get_wtime() и увеличите размер проблемы, оно все равно не будет хорошо масштабироваться.Это связано с тем, что ЦП выполняет очень мало вычислений и много массива записывает в память (доступ к основной памяти медленный - отсутствует кеш).

Как заметил Жан-Клод Арбо, ваш порядок петель неверен и делает доступ к памяти еще медленнее.Некоторые компиляторы могут изменить это для вас с более высокими уровнями оптимизации (-O2 или -O3), но только с некоторыми из них.

И еще хуже, как отметил Джим Cownie, у вас есть гонка состояние .Несколько потоков пытаются использовать один и тот же s для чтения и записи, и программа недействительна.Вам нужно сделать s приватным, используя private(s).


. С помощью приведенных выше исправлений я получаю примерно в два раза более быстрый параллельный участок с четырьмя ядрами и четырьмя потоками.Не пытайтесь использовать гиперпоточность, это замедляет работу программы.

Если вы даете ЦП больше вычислительной работы, например, s = Bessel_J0(x)/Bessel_J1(y), он масштабируется довольно хорошо для меня, почти в четыре раза быстрее с четырьмяпотоки, и гиперпоточность немного ускоряет его.


Наконец, я предлагаю просто убрать ручную настройку количества потоков, это неудобно для тестирования.Если вы удалите это, вы можете легко использовать OMP_NUM_THREADS=4 ./a.out.

...