Сокращение массива 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.