| |
Пример 6.6
Следующий пример показывает обобщенный итерационный код с потерей
синхронизации, который использует fence
синхронизацию. Окно в
каждом процессе состоит из массива A
, который содержит буферы
адресата и инициатора, выполняющего вызовы put
.
... while(!converged(A)) { update(A); MPI_Win_fence(MPI_MODE_NOPRECEDE, win); for(i=0; i < toneighbors; i++) { MPI_Put(&frombuf[i], 1, fromtype[i], toneighbor[i], todisp[i], 1, totype[i], win); } MPI_Win_fence((MPI_MODE_NOSTORE | MPI_MODE_NOSUCCEED), win); }
Такой же код можно написать лучше с get
, чем с put
. Отметим,
что во время коммуникационной фазы каждое окно одновременно читается (как
put
буфер инициатора) и перезаписывается (как put
буфер адресата). Это допустимо при условии, что нет перекрытий между
put
буфером адресата и другим коммуникационным буфером.
Пример 6.7 Это тот же обобщенный пример, с большим перекрытием вычислений и коммуникаций. Мы полагаем что фаза обновления разбивается на две подфазы: первая, где обновляется ``граница'', которая вовлечена во взаимодействие, и вторая, где обновляется ``ядро'', которое не использует и не предоставляет переданных данных.
... while(!converged(A)) { update_boundary(A); MPI_Win_fence((MPI_MODE_NOPUT | MPI_MODE_NOPRECEDE), win); for(i=0; i < fromneighbors; i++) { MPI_Get(&tobuf[i], 1, totype[i], fromneighbor[i], fromdisp[i], 1, fromtype[i], win); } update_core(A); MPI_Win_fence(MPI_MODE_NOSUCCEED, win); }
get
коммуникации могут происходить одновременно с обновлением ядра,
в том случае, если они не обращаются к одним и тем же местам, и локальное
обновление буфера инициатора вызовом get
может происходить
конкурентно с локальным обновлением ядра при помощи вызова
update_core
. Чтобы получить похожее перекрытие, используя
put
коммуникации, мы должны использовать отдельные окна для ядра и
границы. Это необходимо в связи с тем, что мы не позволяем локальным
stores
происходить одновременно с вызовами put
в одном, или
в перекрывающихся окнах.
Пример 6.8
Это тот же код, как и в Примере 6.7, переписанный с использованием post-start-complete-wait.
... while(!converged(A)) { update(A); MPI_Win_post(fromgroup, 0, win); MPI_Win_start(togroup, 0, win); for(i=0; i < toneighbors; i++) { MPI_Put(&frombuf[i], 1, fromtype[i], toneighbor[i], todisp[i], 1, totype[i], win); } MPI_Win_complete(win); MPI_Win_wait(win); }
Пример 6.9 Это тот же пример, что и 6.7 , но с разделенными фазами.
... while(!converged(A)) { update_boundary(A); MPI_Win_post(togroup, MPI_MODE_NOPUT, win); MPI_Win_start(fromgroup, 0, win); for(i=0; i < fromneighbors; i++) { MPI_Get(&tobuf[i], 1, totype[i], fromneighbor[i], fromdisp[i], 1, fromtype[i], win); } update_core(A); MPI_Win_complete(win); MPI_Win_wait(win); }
Пример 6.10
Коммуникационная модель с двойной буферизацией
(шахматная доска - checkerboard), которая позволяет большее
перекрытие вычислений и коммуникаций. Массив A0
обновляется с
использованием значения из массива A1
, и наоборот. Мы полагаем, что
коммуникации симметричны: если процесс A
получает данные от
процесса B
, то процесс B
получает данные от процесса
A
. Окно win
состоит из массива Ai
.
... if (!converged(A0,A1)) { MPI_Win_post(neighbors, (MPI_MODE_NOCHECK | MPI_MODE_NOPUT), win0); } MPI_Barrier(comm0); /* Барьер необходим, потому что вызов START в цикле использует */ /* опцию nocheck */ while(!converged(A0, A1)) { /* Связь на A0 и вычисление на A1 */ update2(A1, A0); /* Локальная модификация A1, */ /* которая зависит от A0 (и A1) */ MPI_Win_start(neighbors, MPI_MODE_NOCHECK, win0); for(i=0; i < neighbors; i++) { MPI_Get(&tobuf0[i], 1, totype0[i], neighbor[i], fromdisp0[i], 1, fromtype0[i], win0); } update1(A1); /* Локальное обновление A1, которое происходит */ /* параллельно с коммуникациями, обновляющими A0 */ MPI_Win_post(neighbors, (MPI_MODE_NOCHECK | MPI_MODE_NOPUT), win1); MPI_Win_complete(win0); MPI_Win_wait(win0); /* Связь на A1 и вычисление на A0 */ update2(A0, A1); /* Локальная модификация A0, */ /* которая зависит от A1 (и A0) */ MPI_Win_start(neighbors, MPI_MODE_NOCHECK, win1); for(i=0; i < neighbors; i++) { MPI_Get(&tobuf1[i], 1, totype1[i], neighbor[i], fromdisp1[i], 1, fromtype1[i], win1); } update1(A0); /* Локальное обновление A0, которое происходит */ /* параллельно с коммуникациями, обновляющимb A1 */ if (!converged(A0,A1)) { MPI_Win_post(neighbors, (MPI_MODE_NOCHECK | MPI_MODE_NOPUT), win0); } MPI_Win_complete(win1); MPI_Win_wait(win1); }
Процесс выделяет для доступа локальное окно, связанное с win0
,
перед тем как он выполняет RMA обращения к удаленным окнам, связанным
с win1
. Когда произойдет возврат из вызова WAIT(win1)
,
тогда все соседи вызвавшего процесса запостили окна, связанные с
win0
. Наоборот, когда вызов wait(win0)
возвращается, тогда
все соседи вызывающего процесса уже выставят для доступа окна, связанные с
win1
. Следовательно, опция noncheck
может использоваться с
вызовами MPI_WIN_START
.
Вызовы put
могут использоваться вместо вызовов get
, если
область массива A0
(соотв. A1
), используемая вызовом
update(A1,A0)
(соотв. Update(A0,A1)
), отделена от области,
изменённой при RMA коммуникациях. На некоторых системах, вызов
put
может быть более эффективен, чем вызов get
, так как он
требует информационного обмена только в одном направлении.
Закладки на сайте Проследить за страницей |
Created 1996-2024 by Maxim Chirkov Добавить, Поддержать, Вебмастеру |