Требуются ли блокировки для всех вызовов MPI при использовании MPI_THREAD_SERIALIZED в многопоточной среде? - PullRequest
1 голос
/ 02 июля 2019

В многопоточной среде MPI вызовы MPI должны быть защищены мьютексом (или другим механизмом блокировки потока), когда мы инициализируем MPI_Init_thread с MPI_THREAD_SERIALIZED (проверьте этот ответ ).Это не требуется для MPI_THREAD_MULTIPLE , но это поддерживается не всеми реализациями MPI.

Мой вопрос заключается в том, является ли блокировка абсолютно необходимой для некоторых функций MPI, особенно для MPI_Test , MPI_Wait и MPI_Get_count .Я знаю, что блокировка требуется для всех вызовов MPI "со связью" (например, MPI_Gather , MPI_Bcast , MPI_Send , MPI_Recv , MPI_Isend , MPI_Irecv и т. Д.), Но я подозреваю, что эта блокировка не требуется для других функций, таких как MPI_Get_count , то есть локальной функции.Мне нужно знать, требуется ли эта блокировка для таких функций, как MPI_Test , MPI_Wait , MPI_Get_count , MPI_Probe и MPI_Iprobe (я не знаю, какие из них являются локальными функциями, а какие нет).Эта зависимость блокировки определена в стандарте MPI или определяется реализацией?

Я занимаюсь разработкой библиотеки распараллеливания с неблокирующими вызовами MPI, смешанными с потоками C ++ 11, и мне нужно использовать MPI_THREAD_SERIALIZED для поддержки большинства реализаций MPI. MPI_THREAD_MULTIPLE также реализован (в большинстве случаев более высокая производительность) в библиотеке, но также требуется поддержка MPI_THREAD_SERIALIZED .

В следующем простом примере кода требуетсяблокировка перед вызовом MPI_Test ?

#include <mutex>
#include <vector>
#include <thread>
#include <iostream>
#include <mpi.h>

static std::mutex mutex;
const static int numThreads = 4;
static int rank;
static int nprocs;

static void rthread(const int thrId) {
    int recv_buff[2];
    int send_buff[2];
    MPI_Request recv_request;

    {
        std::lock_guard<std::mutex> lck(mutex);     // <-- this lock is required
        MPI_Irecv(recv_buff, 2, MPI_INT, ((rank>0) ? rank-1 : nprocs-1), thrId, MPI_COMM_WORLD, &recv_request);
    }

    send_buff[0] = thrId;
    send_buff[1] = rank;
    {
        std::lock_guard<std::mutex> lck(mutex);     // <-- this lock is required
        MPI_Send(send_buff, 2, MPI_BYTE, ((rank+1<nprocs) ? rank+1 : 0), thrId, MPI_COMM_WORLD);
    }

    int flag = 0;
    while (!flag) {
        std::lock_guard<std::mutex> lck(mutex);    // <-- is this lock required?
        MPI_Test(&recv_request, &flag, MPI_STATUS_IGNORE);
        //...        do other stuff
    }

    std::cout << "[Rank " << rank << "][Thread " << thrId << "] Received a msg from thread " << recv_buff[0] << " from rank " << recv_buff[1] << std::endl;

}

int main(int argc, char **argv) {
    int provided;

    MPI_Init_thread(&(argc), &(argv), MPI_THREAD_SERIALIZED, &provided);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    MPI_Comm_size(MPI_COMM_WORLD, &nprocs);

    std::vector<std::thread> threads;
    for(int threadId = 0; threadId < numThreads; threadId++) {
        threads.push_back(std::thread(rthread, threadId));
    }
    for(int threadId = 0; threadId < numThreads; threadId++) {
        threads[threadId].join();
    }
    MPI_Finalize();
}

В своих тестах я выполнил код без блокировок при вызовах MPI_Test и MPI_Get_count , ничегослучилось что-то плохое и производительность улучшилась, но я не знаю, нормально это или нет.

1 Ответ

1 голос
/ 02 июля 2019

Требуется блокировка.Стандарт просто кратко заявляет:

MPI_THREAD_SERIALIZED Процесс может быть многопоточным, и несколько потоков могут выполнять вызовы MPI, но только по одному за раз: вызовы MPIне производится одновременно из двух отдельных потоков

Таким образом, нет различия между вызовами различных видов функций MPI.Поскольку вы стремитесь написать переносимый код - в противном случае вы можете просто предположить реализацию с MPI_THREAD_MULTIPLE - вы должны придерживаться стандарта.

...