在多线程环境中使用 MPI_THREAD_SERIALIZED 时,所有 MPI 调用都需要锁吗?
Are locks required for all MPI calls when using MPI_THREAD_SERIALIZED in multithread enviroment?
在 MPI 多线程环境中,当我们使用 [= 初始化 MPI_Init_thread 时,MPI 调用应使用互斥锁(或其他线程锁定机制)进行保护73=](检查 this answer)。 MPI_THREAD_MULTIPLE 不需要这样做,但并非所有 MPI 实现都支持。
我的问题是某些MPI函数是否绝对需要锁,特别是MPI_Test、MPI_Wait 和 MPI_Get_count。我知道所有 MPI 调用都需要锁定 "with communication"(例如 MPI_Gather、MPI_Bcast, MPI_Send, MPI_Recv, MPI_Isend, MPI_Irecv, etc), 但是我怀疑其他函数不需要这个锁,比如MPI_Get_count, 即一个局部函数。我需要知道 MPI_Test、MPI_Wait、[=23= 等函数是否需要此锁]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 调用中执行了一些没有锁定的代码,没什么不好的发生了并且性能提高了,但我不知道这是否可以。
需要锁。该标准只是简要说明:
MPI_THREAD_SERIALIZED
The process may be multi-threaded, and multiple
threads may make MPI calls, but only one at a time: MPI calls are not
made concurrently from two distinct threads
所以对不同种类的MPI函数的调用没有区别。由于您的目标是编写可移植代码 - 否则您可以假设使用 MPI_THREAD_MULTIPLE
实现 - 您必须坚持标准。
在 MPI 多线程环境中,当我们使用 [= 初始化 MPI_Init_thread 时,MPI 调用应使用互斥锁(或其他线程锁定机制)进行保护73=](检查 this answer)。 MPI_THREAD_MULTIPLE 不需要这样做,但并非所有 MPI 实现都支持。
我的问题是某些MPI函数是否绝对需要锁,特别是MPI_Test、MPI_Wait 和 MPI_Get_count。我知道所有 MPI 调用都需要锁定 "with communication"(例如 MPI_Gather、MPI_Bcast, MPI_Send, MPI_Recv, MPI_Isend, MPI_Irecv, etc), 但是我怀疑其他函数不需要这个锁,比如MPI_Get_count, 即一个局部函数。我需要知道 MPI_Test、MPI_Wait、[=23= 等函数是否需要此锁]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 调用中执行了一些没有锁定的代码,没什么不好的发生了并且性能提高了,但我不知道这是否可以。
需要锁。该标准只是简要说明:
MPI_THREAD_SERIALIZED
The process may be multi-threaded, and multiple threads may make MPI calls, but only one at a time: MPI calls are not made concurrently from two distinct threads
所以对不同种类的MPI函数的调用没有区别。由于您的目标是编写可移植代码 - 否则您可以假设使用 MPI_THREAD_MULTIPLE
实现 - 您必须坚持标准。