У меня есть специфическая проблема с MPI, когда массив, на который не ссылается команда MPI, перезаписывается --- происходит какая-то проблема с памятью.
В первом случае с collectv, mpi работает как положено. При втором вызове collectv затрагивается информация из первого массива!
Код, над которым я работаю, довольно большой, однако я создал отдельную программу, которая примерно показывает проблему.
Однако в меньшей программе, несмотря на наличие проблемы, программа вызывает ошибку сегмента, а не просто продолжает работу, как это делает большая программа.
program main
use mpi
integer :: chunksize, send_count, i_start, i_end
integer, allocatable :: rec_starts(:), rec_counts(:)
integer, parameter :: dp = 8; ! double precision
REAL(DP), allocatable:: array_2d(:,:)
REAL(DP), allocatable:: array_3d(:,:,:)
INTEGER, parameter:: num_skill=5, num_pref=2
INTEGER, parameter:: num_ed=3, num_children=2, num_age=4, num_market=28, num_health=2, num_year=2
INTEGER, parameter:: num_total_state_m=num_children*num_market*num_year*num_ed*num_age*num_health*num_ed*num_age*num_health
real(dp), dimension(num_skill,num_total_state_m) :: array_2d_local
real(dp), dimension(num_pref,num_pref,num_total_state_m) :: array_3d_local
integer i,j,k,l,m
!mpi vars
integer :: ierr, ntasks, mpi_id
! Set up MPI
call mpi_init(ierr)
call mpi_comm_size(mpi_comm_world, ntasks, ierr) !get number of tasks
call mpi_comm_rank(mpi_comm_world, mpi_id, ierr) !get id of each task
write(*,*) 'process ', mpi_id+1, 'of ', ntasks, 'is alive,', ' mpi_id:',mpi_id
!calculate which 'i' this thread is responsible for
chunksize = (num_total_state_m + ntasks - 1) / ntasks !note int/int rounds down
i_start = (mpi_id)*chunksize + 1
i_end = min((mpi_id+1)*chunksize,num_total_state_m)
!set up practice matrices
allocate(array_2d(num_skill,num_total_state_m), &
array_3d(num_pref,num_pref,num_total_state_m))
l = 1
m = -1
do i=1,num_skill
do j=1, num_total_state_m
if (mpi_id==0) array_2d_local(i,j) = l
if (mpi_id==1) array_2d_local(i,j) = m
l = l + 1
m = m - 1
end do
end do
l = 1
m = -1
do i=1, num_pref
do j=1, num_pref
do k=1, num_total_state_m
if (mpi_id==0) array_3d_local(i,j,k) = l
if (mpi_id==1) array_3d_local(i,j,k) = m
l = l + 1
m = m - 1
end do
end do
end do
! Next send matricies
allocate(rec_starts(ntasks), rec_counts(ntasks))
do i=1, ntasks
rec_counts(i) = min(num_total_state_m, i * chunksize) - (i-1)*chunksize
rec_starts(i) = (i-1) * chunksize
end do
rec_counts = rec_counts * num_skill
rec_starts = rec_starts * num_skill
send_count = rec_counts(mpi_id+1)
! -m (dimensions:num_skill, num_total_state_m) double
call mpi_gatherv(array_2d_local(:,i_start:i_end), send_count, &
mpi_double_precision, &
array_2d, rec_counts, rec_starts, mpi_double_precision, &
0, mpi_comm_world, ierr)
! Next do 3d array
! IF THESE LINES ARE UNCOMMENTED, THE PROGRAM WORKS FINE!
!do i=1, ntasks
! rec_counts(i) = min(num_total_state_m, i * chunksize) - (i-1)*chunksize
! rec_starts(i) = (i-1) * chunksize
!end do
rec_counts = rec_counts * num_pref
rec_starts = rec_starts * num_pref
send_count = rec_counts(mpi_id+1)
! -array_3d (num_pref,num_pref,num_total_state_m)double
print*, array_2d(1,1), mpi_id, 'before'
call mpi_gatherv(array_3d_local(:,:,i_start:i_end), send_count, &
mpi_double_precision, &
array_3d, rec_counts, rec_starts, mpi_double_precision, &
0, mpi_comm_world, ierr)
print*, array_2d(1,1), mpi_id, 'after'
deallocate(rec_starts, rec_counts)
deallocate(array_2d, array_3d)
end program main
Вывод в этой небольшой программе выглядит так:
mpifort -fcheck=all -fbacktrace -g -Og -ffree-line-length-2048 main.f90 -o run_main
mpiexec -np 2 run_main 2>&1 | tee run_main.log
process 1 of 2 is alive, mpi_id: 0
process 2 of 2 is alive, mpi_id: 1
1.0000000000000000 0 before
0.0000000000000000 1 before
Program received signal SIGSEGV: Segmentation fault - invalid memory reference.
Backtrace for this error:
#0 0x101e87579
#1 0x101e86945
#2 0x7fff6a9ecb5c
В более крупной программе, где программа не работает с ошибками, вывод на печать выглядит примерно так.
1.0000000000000000 0 before
0.0000000000000000 1 before
-1.9018063100806379 0 after
0.0000000000000000 1 after
Я смотрел на другие сообщения SO: MPI_Recv перезаписывает части памяти, к которым он не должен обращатьсяMPI_Recv перезаписывает части памяти, к которым он не должен обращаться
но, как не специалист по fortran/mpi, к сожалению, мне недостаточно ответов на эти сообщения, чтобы понять проблему.
Любая помощь или понимание очень ценятся. Спасибо!
Редактировать: Спасибо, просто я идиот. Если кто-то еще столкнется с этим, трижды проверьте свои recvcounts
и displs
!
Сейчас попробую собрать, спасибо. Я думаю, что это проблема указателя с rec_starts, где во второй раз он используется, он указывает на предыдущую память. Более полный код должен сделать это более ясным и легким для исправления, вы правы.
Привет, я обновил пост с отдельным примером. Я также нашел «исправление», но я все же хотел бы понять, является ли это известной проблемой или в чем заключается сделка, чтобы я не попадал в эту ошибку в других случаях, когда я мог не знать об ошибке.
Ваш исходный код делает
do i=1, ntasks
rec_counts(i) = min(num_total_state_m, i * chunksize) - (i-1)*chunksize
rec_starts(i) = (i-1) * chunksize
end do
rec_counts = rec_counts * num_skill
rec_starts = rec_starts * num_skill
send_count = rec_counts(mpi_id+1)
а потом
rec_counts = rec_counts * num_pref
rec_starts = rec_starts * num_pref
send_count = rec_counts(mpi_id+1)
вы просто забыли разделить на num_skill
.
тривиальное исправление - заменить последние три строки на
rec_counts = rec_counts * num_pref / num_skill
rec_starts = rec_starts * num_pref / num_skill
send_count = rec_counts(mpi_id+1)
Если вы подозреваете ошибку в библиотеке MPI, рекомендуется попробовать другую (например, MPICH (производную) и Open MPI). Если ваше приложение вылетает с обоими, то, скорее всего, ошибка в вашем приложении.
Пожалуйста, покажите нам свою полную программу или хотя бы минимальный пример, показывающий проблему, с которой вы столкнулись. В частности, здесь, не видя, как распределяются различные массивы, невозможно ответить на ваш вопрос, но в целом без полной программы может быть очень сложно и много времени проверять идеи перед публикацией - и, следовательно, эти идеи никогда не публикуются...