在MPI多线程环境中,当我们使用 MPI_THREAD_SERIALIZED 初始化 MPI_Init_thread 时,应使用互斥锁(或其他线程锁定机制)保护MPI调用(选中this answer) 。 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标准中定义了这种锁依赖关系还是在实现中定义了?
我正在开发一个并行库,该库具有与C ++ 11线程混合的无阻塞MPI调用,并且我需要使用 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 调用中执行了一些没有锁的代码,没有发生任何不良情况,并且性能有所提高,但是我不知道这样做是否可以是否。
答案 0 :(得分:1)
需要锁定。该标准只是简要说明:
MPI_THREAD_SERIALIZED
:该过程可能是多线程的,并且有多个 线程可以进行MPI调用,但一次只能进行一次:MPI调用不是 由两个不同的线程同时进行
因此,对不同种类的MPI函数的调用之间没有区别。由于您打算编写可移植的代码-否则,您可以假设使用MPI_THREAD_MULTIPLE
实现-您必须遵守标准。