next up previous contents
Next: Обработка ошибок Up: Односторонние взаимодействия Previous: Различные пояснения   Contents

Примеры

Пример 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$_i$ состоит из массива 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, так как он требует информационного обмена только в одном направлении.



Alex Otwagin 2002-12-10