Сокращение массива Openmp с помощью Fortran

Я пытаюсь распараллелить код, который я написал. У меня возникают проблемы с выполнением редуктонов на массивах. Кажется, все работает отлично для небольших массивов, однако, когда размер массива превышает определенную точку, я получаю ошибку или сбой.

Я попытался увеличить размер стека с помощью /F во время компиляции, я использую ifort в окнах, я также попытался передать set KMP_STACKSIZE = xxx объявленную спецификацию стеков в Intel. Это иногда помогает и позволяет коду продвигаться дальше через мой цикл, но в конце концов не решает проблему даже при размере стека 1 Гбит или больше.

Ниже приведен небольшой самостоятельный рабочий пример моего кода. Он работает в серийном режиме и с одним потоком. Или со многими потоками, но с небольшим "N". Большой N (то есть, например, 250 000 в примере) вызывает проблемы.

Я не думал, что эти массивы были настолько массивными, чтобы вызвать серьезные проблемы, и предполагалось, что увеличение пространства в стеке поможет - есть ли какие-либо другие варианты или я пропустил что-то важное в моем кодировании?

program testreduction use omp_lib implicit none integer :: i, j, nthreads, Nsize integer iseed /3/ REAL, allocatable :: A(:,:), B(:), C(:), posi(:,:) REAL :: dx, dy, r, Axi, Ayi, m, F !Set size of matrix, and main loop Nsize = 250000 m = 1.0 F = 1.0 !Allocate posi array allocate(posi(2,Nsize)) !Fill with random numbers do i=1,Nsize do j=1,2 posi(j,i) = (ran(iseed)) end do end do !Allocate other arrays allocate(A(2,Nsize), C(Nsize), B(Nsize)) print*, sizeof(A)+sizeof(B)+sizeof(C) !$OMP parallel nthreads = omp_get_num_threads() !$OMP end parallel print*, "Number of threads ", nthreads !Go through each array and do some work, calculating a reduction on A, B and C. !$OMP parallel do schedule(static) private(i, j, dx, dy, r, Axi, Ayi) reduction(+:C, B, A) do i=1,Nsize do j=1,Nsize !print*, i dx = posi(1,i) - posi(1,j) dy = posi(2,i) - posi(2,j) r = sqrt(dx**2+dy**2) Axi = -m*(F)*(dx/(r)) Ayi = -m*(F)*(dy/(r)) A(1,i) = A(1,i) + Axi A(2,i) = A(2,i) + Ayi B(i) = B(i) + (Axi+Ayi) C(i) = C(i) + dx/(r) + dy/(r) end do END DO !$OMP END parallel do
end program

ОБНОВИТЬ

Лучший пример того, о чем я говорю.

program testreduction2 use omp_lib implicit none integer :: i, j, nthreads, Nsize, q, k, nsize2 REAL, allocatable :: A(:,:), B(:), C(:) integer, ALLOCATABLE :: PAIRI(:), PAIRJ(:) Nsize = 25 Nsize2 = 19 q=0 allocate(A(2,Nsize), C(Nsize), B(Nsize)) ALLOCATE(PAIRI(nsize*nsize2), PAIRJ(nsize*nsize2)) do i=1,nsize do j =1,nsize2 q=q+1 PAIRI(q) = i PAIRJ(q) = j end do end do A = 0 B = 0 C = 0 !$OMP parallel do schedule(static) private(i, j, k) do k=1,q i=PAIRI(k) j=PAIRJ(k) A(1,i) = A(1,i) + 1 A(2,i) = A(2,i) + 1 B(i) = B(i) + 1 C(i) = C(i) + 1 END DO !$OMP END parallel do PRINT*, A PRINT*, B PRINT*, C
END PROGRAM
1 ответ

Проблема в том, что вы уменьшаете действительно большие массивы. Обратите внимание, что другие языки (C, C++) не могут вообще уменьшать массивы.

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

Попробуйте просто

!$OMP parallel do schedule(static) private(i, dx, dy, r, Axi, Ayi)
do i=1,Nsize do j=1,Nsize ... A(1,i) = A(1,i) + Axi A(2,i) = A(2,i) + Ayi B(i) = B(i) + (Axi+Ayi) C(i) = C(i) + dx/(r) + dy/(r) end do
end do
!$OMP END parallel do

Дело в том, что потоки не взаимодействуют. Каждый поток использует различный набор i и, следовательно, различные элементы A, B и C

Даже если вы придумаете случай, когда это кажется необходимым, вы всегда можете переписать его, чтобы избежать его. Вы можете даже выделить некоторые буферы самостоятельно и имитировать сокращение. Или используйте атомные обновления.

licensed under cc by-sa 3.0 with attribution.