Синхронизация доступа к общей памяти MPI3: гарантированно ли работает этот код по стандартам MPI? - PullRequest
2 голосов
/ 19 февраля 2020

Стандарт MPI-3 представляет общую память, которая может быть прочитана и записана всеми процессами, разделяющими эту память, без использования обращений к библиотеке MPI. Хотя есть примеры одностороннего обмена данными с использованием разделяемой или не разделяемой памяти, я не нашел много информации о том, как правильно использовать разделяемую память с прямым доступом.

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

// initialization:
MPI_Comm comm_shared;
MPI_Comm_split_type(MPI_COMM_WORLD, MPI_COMM_TYPE_SHARED, i_mpi, MPI_INFO_NULL, &comm_shared);

// allocation
const int N_WIN=10;
const int mem_size = 1000*1000;
double* mem[10];
MPI_Win win[N_WIN];
for (int i=0; i<N_WIN; i++) {   // I need several buffers.
    MPI_Win_allocate_shared( mem_size, sizeof(double), MPI_INFO_NULL, comm_shared, &mem[i], &win[i] );
    MPI_Win_lock_all(0, win);
}

while(1) {
    MPI_Barrier(comm_shared);
    ... // write anywhere on shared memory
    MPI_Barrier(comm_shared);
    ... // read on shared memory written by other processes
}

// deallocation
for (int i=0; i<N_WIN; i++) {
    MPI_Win_unlock_all(win[i]);
    MPI_Win_free(&win[i]);
}

Здесь я обеспечиваю синхронизацию с помощью MPI_Barrier() и предполагаю, что аппаратное обеспечение обеспечивает согласованность представления памяти. Кроме того, поскольку у меня есть несколько общих windows, один вызов MPI_Barrier кажется более эффективным, чем вызов MPI_Win_fence() в каждом окне общей памяти.

Кажется, он хорошо работает на моих ноутбуках и серверах x86. Но является ли эта программа действительной / правильной программой MPI? Есть ли более эффективный метод достижения того же самого?

Ответы [ 2 ]

2 голосов
/ 06 марта 2020

Здесь есть две ключевые проблемы:

  1. MPI_Barrier абсолютно не является барьером памяти и никогда не должен использоваться таким образом. Он может синхронизировать память как побочный эффект ее реализации в большинстве случаев, но пользователи никогда не могут этого допустить. MPI_Barrier гарантируется только синхронизация выполнения процесса. (Если это поможет, вы можете представить себе систему, в которой MPI_Barrier реализован с использованием аппаратного виджета, который не превышает требования стандарта MPI. В некоторых случаях IBM Blue Gene делал это.)
  2. Этот вопрос невозможно ответить без подробностей о том, что вы на самом деле делаете с разделяемой памятью:
while(1) {
    MPI_Barrier(comm_shared);
    ... // write anywhere on shared memory
    MPI_Barrier(comm_shared);
    ... // read on shared memory written by other processes
}

Возможно, это не написано четко, но это было предположено авторами соответствующего текста MPI- 3 стандарта - я был частью этой группы - что можно рассуждать о разделяемой памяти, используя модель памяти базового / основного языка. Таким образом, если вы пишете этот код на C11, вы можете рассуждать об этом в соответствии с моделью памяти C11.

Если вы хотите использовать MPI для синхронизации совместно используемой памяти, то вы должны использовать MPI_Win_sync на всех windows для доступа к хранилищу и MPI_Win_flush для операций RMA (Put / Get / Accumulate / Get_accumulate / Fetch_and_op / Compare_and_swap).

Я ожидаю MPI_Win_sync должен быть реализован как барьер памяти ЦП, поэтому избыточно вызывать его для каждого окна. Вот почему может быть более эффективно использовать модели памяти C11 или C ++ 11 и использовать https://en.cppreference.com/w/c/atomic/atomic_thread_fence и https://en.cppreference.com/w/cpp/atomic/atomic_thread_fence соответственно.

1 голос
/ 19 февраля 2020

Я хотел бы сказать, что эта программа MPI недействительна.

Чтобы объяснить, на чем я основываю свое мнение

  • В описании MPI_Win_create_shared:

    Последовательность обращений к загрузке / сохранению из / в разделяемую память, наблюдаемая пользовательской программой, зависит от архитектуры. Согласованное представление может быть создано в модели унифицированной памяти (см. Раздел 11.4) путем использования функций синхронизации окон (см. Раздел 11.5) или явного завершения незавершенных обращений к хранилищу (например, путем вызова MPI_WIN_FLU SH). MPI не определяет семантику для доступа к разделяемой памяти windows в отдельной модели памяти.

  • Раздел 11.4, о моделях памяти, в котором говорится:

    В унифицированной модели RMA публичные c и частные копии идентичны, и обновления с помощью вызовов пут или накопления в конечном итоге наблюдаются операциями загрузки без дополнительных вызовов RMA. Доступ магазина к окну в конечном итоге виден для удаленного получения или накопления вызовов без дополнительных вызовов RMA. Эта более строгая семантика унифицированной модели RMA позволяет пользователю пропускать некоторые вызовы синхронизации и потенциально повышает производительность.

  • В приведенных ниже рекомендациях для пользователей указано только:

    Если доступы в унифицированной модели RMA не синхронизированы (с блокировками или сбросами, см. Раздел 11.5.3), операции загрузки и сохранения могут наблюдать изменения в памяти во время их выполнения.

  • Раздел 11.7, semanti c и правильность говорит:

    MPI_BARRIER обеспечивает синхронизацию процесса, но не синхронизацию с памятью.

  • Различные примеры в 11.8 хорошо объясняют, как использовать операции flu sh и syn c.

Единственная когда-либо адресованная синхронизация всегда и только односторонняя т. е. в вашем случае MPI_Win_flush{,_all} или MPI_Win_unlock{,_all} (за исключением взаимного исключения активной и пассивной параллельной синхронизации, которое должно выполняться пользователем, или использования MP I_MODE_NOCHECK флаг подтверждения).

Таким образом, либо вы обращаетесь непосредственно к памяти с хранилищем, и вам необходимо вызвать MPI_Win_sync() для каждого из ваших windows перед вызовом MPI_Barrier (как описано в примере 11.10), чтобы обеспечить синхронизацию или вы осуществляете доступ к RMA, и тогда вам придется позвонить по крайней мере MPI_Win_flush_all перед вторым барьером, чтобы убедиться, что операции распространены. Если вы пытаетесь читать, используя операцию загрузки, вам, возможно, придется выполнить синхронизацию после второго барьера, а затем сделать это.

Другое решение - разблокировка и повторная блокировка между барьерами или использование компилятора и аппаратного обеспечения. c нотации могут обеспечить загрузку после обновления данных.

...