为什么mpi消息传递速度似乎加快了?

为什么mpi消息传递速度似乎加快了?,mpi,benchmarking,Mpi,Benchmarking,我正在学习编程MPI,我制作了一个简单的程序来来回传递消息。在消息中,我记录了发送和接收的时间(以纳秒为单位),我注意到了一些奇怪的事情:在最初几次发送/接收消息时,有很大的延迟(几十微秒),尽管发送/接收的次数越多,延迟消失了,只有1-2微秒为什么会发生这种情况? 我的程序运行在一台有四个内核的机器上,我用其中两个内核调用这个程序。我创建了一个简单的示例来演示: vector<size_t> times; times.reserve(100); stopwatch s;//Reco

我正在学习编程MPI,我制作了一个简单的程序来来回传递消息。在消息中,我记录了发送和接收的时间(以纳秒为单位),我注意到了一些奇怪的事情:在最初几次发送/接收消息时,有很大的延迟(几十微秒),尽管发送/接收的次数越多,延迟消失了,只有1-2微秒为什么会发生这种情况?

我的程序运行在一台有四个内核的机器上,我用其中两个内核调用这个程序。我创建了一个简单的示例来演示:

vector<size_t> times;
times.reserve(100);
stopwatch s;//Records time since initialization of value
int counter = 0;
if(mpi.world_rank == 0)
{
    //Do this if you're on thread 0
    for(int i=0;i<20;++i)
    {
        ++counter;
        times.push_back(s.age_nano());
        //Send counter (size of 1) to thread 1 with tag 0
        mpi.send(&counter, 1, 1, 0);
        //Receive value (size of 1) from thread 1 with tag 0
        mpi.receive(&counter, 1, 1, 0);
    }
}
else if(mpi.world_rank == 1)
{
    //Otherwise do this if you're on thread 1
    for(int i=0;i<20;++i)
    {
        //Receive value (size of 1) from thread 0 with tag 0
        mpi.receive(&counter, 1, 0, 0);
        ++counter;
        times.push_back(s.age_nano());
        //Send counter (size of 1) to thread 0 with tag 0
        mpi.send(&counter, 1, 0, 0);
    }
}
for(int i=times.size(); i > 0; --i) times[i] -= times[i-1];
cout << times << " Counter: " << counter << endl;
如果您注意到,前几个值中的一些值比其他值高得多,大多数值在500到700纳秒之间。
mpi.send
mpi.receive
函数只是一个非常轻巧的包装器,用于更标准的函数,如
mpi\u send
mpi\u Recv
。以下是秒表类的代码:

struct stopwatch
{
typedef decltype(std::chrono::high_resolution_clock::now()) time;
typedef std::chrono::duration<double, std::ratio<1,1>> seconds;
typedef std::chrono::duration<double, std::milli> milliseconds;
typedef std::chrono::duration<double, std::micro> microseconds;
typedef std::chrono::duration<double, std::nano> nanoseconds;
time _start = std::chrono::high_resolution_clock::now();
auto age_nano()
{
    return (std::chrono::high_resolution_clock::now() - _start).count();
}
double age_micro()
{
    return microseconds(std::chrono::high_resolution_clock::now() - _start).count();
}
double age_milli()
{
    return milliseconds(std::chrono::high_resolution_clock::now() - _start).count();
}
double age()
{
    return seconds(std::chrono::high_resolution_clock::now() - _start).count();
}
void reset() { _start = std::chrono::high_resolution_clock::now(); }
};
struct秒表
{
typedef decltype(std::chrono::high_resolution_clock::now())时间;
typedef std::chrono::持续时间秒;
typedef std::chrono::duration毫秒;
typedef std::chrono::持续时间微秒;
typedef std::chrono::持续时间纳秒;
时间开始=标准::时钟::高分辨率时钟::现在();
汽车时代
{
返回(std::chrono::high_resolution_clock::now()-_start).count();
}
双龄_-micro()
{
返回微秒(std::chrono::high_resolution_clock::now()-_start).count();
}
双倍年龄
{
返回毫秒(std::chrono::high_resolution_clock::now()-_start).count();
}
双倍年龄()
{
返回秒数(std::chrono::high_resolution_clock::now()-_start).count();
}
void reset(){u start=std::chrono::high_resolution_clock::now()}
};
下面是我围绕mpi构建的包装器的代码:

#include <mpi.h>
#include <vector>
#include <string>
template<class...> struct get_mpi_type{};
template<class T> struct get_mpi_type<const T>      { static constexpr auto type() { return get_mpi_type<T>::type(); } };
template<> struct get_mpi_type<short>               { static constexpr auto type() { return MPI_SHORT; }; };
template<> struct get_mpi_type<int>                 { static constexpr auto type() { return MPI_INT; }; };
template<> struct get_mpi_type<long int>            { static constexpr auto type() { return MPI_LONG; }; };
template<> struct get_mpi_type<long long int>       { static constexpr auto type() { return MPI_LONG_LONG; }; };
template<> struct get_mpi_type<unsigned char>       { static constexpr auto type() { return MPI_UNSIGNED_CHAR; }; };
template<> struct get_mpi_type<unsigned short>      { static constexpr auto type() { return MPI_UNSIGNED_SHORT; }; };
template<> struct get_mpi_type<unsigned int>        { static constexpr auto type() { return MPI_UNSIGNED; }; };
template<> struct get_mpi_type<unsigned long int>   { static constexpr auto type() { return MPI_UNSIGNED_LONG; }; };
template<> struct get_mpi_type<unsigned long long int> { static constexpr auto type() { return MPI_UNSIGNED_LONG_LONG; }; };
template<> struct get_mpi_type<float>               { static constexpr auto type() { return MPI_FLOAT; }; };
template<> struct get_mpi_type<double>              { static constexpr auto type() { return MPI_DOUBLE; }; };
template<> struct get_mpi_type<long double>             { static constexpr auto type() { return MPI_LONG_DOUBLE; }; };
template<> struct get_mpi_type<char>                { static constexpr auto type() { return MPI_BYTE; }; };
struct mpi_thread
{
int world_rank;
int world_size;
mpi_thread()
{
    MPI_Init(NULL, NULL);
    MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
    MPI_Comm_size(MPI_COMM_WORLD, &world_size);
}
~mpi_thread()
{
    MPI_Finalize();
}
template<class T> void send(const T* data, int count, int destination, int tag)
{
    MPI_Send(data, count, get_mpi_type<T>::type(), destination, tag, MPI_COMM_WORLD);
}
template<class T> void send(const std::vector<T>& data, int destination, int tag)
{
    send(data.data(), data.size(), destination, tag);
}
template<class T> void send(const std::basic_string<T>& str, int destination, int tag)
{
    send(str.data(), str.size(), destination, tag);
}
MPI_Status probe(int source, int tag)
{
    MPI_Status status;
    MPI_Probe(source, tag, MPI_COMM_WORLD, &status);
    return status;
}
template<class T> int get_msg_size(MPI_Status& status)
{
    int num_amnt;
    MPI_Get_count(&status, get_mpi_type<T>::type(), &num_amnt);
    return num_amnt;
}

template<class T> void receive(T* data, int count, int source, int tag, MPI_Status& status = *MPI_STATUS_IGNORE)
{
    MPI_Recv(data, count, get_mpi_type<T>::type(), source, tag, MPI_COMM_WORLD, &status);
}
template<class T> void receive(std::vector<T>& dest, int source, int tag)
{
    MPI_Status status = probe(source, tag);
    int size = get_msg_size<T>(status);
    dest.clear();
    dest.resize(size);
    receive(&dest[0], size, source, tag, status);
}
template<class T> void receive(std::basic_string<T>& dest, int source, int tag)
{
    MPI_Status status = probe(source, tag);
    int size = get_msg_size<T>(status);
    dest.clear();
    dest.resize(size);
    receive(&dest[0], size, source, tag, status);
}
} mpi;
#包括
#包括
#包括
模板结构get\u mpi\u type{};
模板结构get_mpi_type{static constexpr auto type(){return get_mpi_type::type();};
模板结构get_mpi_type{static constexpr auto type(){return mpi_SHORT;};};
模板结构get_mpi_type{static constexpr auto type(){return mpi_INT;};};
模板结构get_mpi_type{static constexpr auto type(){return mpi_LONG;};};
模板结构get\u mpi\u type{static constexpr auto type(){return mpi\u LONG\u LONG;};};
模板结构get_mpi_type{static constexpr auto type(){return mpi_UNSIGNED_CHAR;};};
模板结构get_mpi_type{static constexpr auto type(){return mpi_UNSIGNED_SHORT;};};
模板结构get_mpi_type{static constexpr auto type(){return mpi_UNSIGNED;};};
模板结构get_mpi_type{static constexpr auto type(){return mpi_UNSIGNED_LONG;};};
模板结构get_mpi_type{static constexpr auto type(){return mpi_UNSIGNED_LONG_LONG;};};
模板结构get_mpi_type{static constexpr auto type(){return mpi_FLOAT;};};
模板结构get_mpi_type{static constexpr auto type(){return mpi_DOUBLE;};};
模板结构get_mpi_type{static constexpr auto type(){return mpi_LONG_DOUBLE;};};
模板结构get_mpi_type{static constexpr auto type(){return mpi_BYTE;};};
结构mpi_线程
{
国际世界排名;
国际大世界;
mpi_线程()
{
MPI_Init(NULL,NULL);
MPI通信等级(MPI通信世界级和世界级);
MPI_Comm_大小(MPI_Comm_WORLD和WORLD_大小);
}
~mpi_线程()
{
MPI_Finalize();
}
模板无效发送(常量T*数据、整数计数、整数目的地、整数标记)
{
MPI_发送(数据、计数、获取MPI_类型::类型()、目标、标记、MPI_通信世界);
}
模板无效发送(const std::vector&data、int destination、int标记)
{
发送(data.data()、data.size()、目标、标记);
}
模板无效发送(const std::basic_string&str、int destination、int标记)
{
发送(str.data()、str.size()、目的地、标记);
}
MPI_状态探测(int源,int标记)
{
MPI_状态;
MPI_探测器(源、标记、MPI_通信世界和状态);
返回状态;
}
模板int获取消息大小(MPI状态和状态)
{
国际数字;
MPI_Get_count(&status,Get_MPI_type::type(),&num_amnt);
返回数值;
}
模板无效接收(T*数据、整数计数、整数源、整数标记、MPI\u状态和状态=*MPI\u状态\u忽略)
{
MPI_Recv(数据、计数、获取MPI_类型::类型()、源、标记、MPI_通信世界和状态);
}
模板无效接收(std::vector&dest、int-source、int-tag)
{
MPI_状态=探头(源、标签);
int size=获取消息大小(状态);
目标清除();
目的地调整大小(大小);
接收(&dest[0]、大小、源、标记、状态);
}
模板无效接收(std::basic_string&dest、int-source、int-tag)
{
MPI_状态=探头(源、标签);
int size=获取消息大小(状态);
目标清除();
目的地调整大小(大小);
接收(&dest[0]、大小、源、标记、状态);
}
}mpi;

此外,我还重载了
ostream如果您想对MPI进行基准测试,可能应该使用(俄亥俄州立大学)OSU基准测试或英特尔IMB等知名基准测试

一些MPI库“按需”建立连接,这意味着首次向对等方发送消息时,建立连接需要额外的开销。当您第一次发送给定的内存区域时,也可能会出现一些开销(必须注册内存,这是有代价的)

众所周知的基准测试通常在进行实际测量之前会运行一些预热迭代,因此结果中会隐藏单次延迟

#include <mpi.h>
#include <vector>
#include <string>
template<class...> struct get_mpi_type{};
template<class T> struct get_mpi_type<const T>      { static constexpr auto type() { return get_mpi_type<T>::type(); } };
template<> struct get_mpi_type<short>               { static constexpr auto type() { return MPI_SHORT; }; };
template<> struct get_mpi_type<int>                 { static constexpr auto type() { return MPI_INT; }; };
template<> struct get_mpi_type<long int>            { static constexpr auto type() { return MPI_LONG; }; };
template<> struct get_mpi_type<long long int>       { static constexpr auto type() { return MPI_LONG_LONG; }; };
template<> struct get_mpi_type<unsigned char>       { static constexpr auto type() { return MPI_UNSIGNED_CHAR; }; };
template<> struct get_mpi_type<unsigned short>      { static constexpr auto type() { return MPI_UNSIGNED_SHORT; }; };
template<> struct get_mpi_type<unsigned int>        { static constexpr auto type() { return MPI_UNSIGNED; }; };
template<> struct get_mpi_type<unsigned long int>   { static constexpr auto type() { return MPI_UNSIGNED_LONG; }; };
template<> struct get_mpi_type<unsigned long long int> { static constexpr auto type() { return MPI_UNSIGNED_LONG_LONG; }; };
template<> struct get_mpi_type<float>               { static constexpr auto type() { return MPI_FLOAT; }; };
template<> struct get_mpi_type<double>              { static constexpr auto type() { return MPI_DOUBLE; }; };
template<> struct get_mpi_type<long double>             { static constexpr auto type() { return MPI_LONG_DOUBLE; }; };
template<> struct get_mpi_type<char>                { static constexpr auto type() { return MPI_BYTE; }; };
struct mpi_thread
{
int world_rank;
int world_size;
mpi_thread()
{
    MPI_Init(NULL, NULL);
    MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
    MPI_Comm_size(MPI_COMM_WORLD, &world_size);
}
~mpi_thread()
{
    MPI_Finalize();
}
template<class T> void send(const T* data, int count, int destination, int tag)
{
    MPI_Send(data, count, get_mpi_type<T>::type(), destination, tag, MPI_COMM_WORLD);
}
template<class T> void send(const std::vector<T>& data, int destination, int tag)
{
    send(data.data(), data.size(), destination, tag);
}
template<class T> void send(const std::basic_string<T>& str, int destination, int tag)
{
    send(str.data(), str.size(), destination, tag);
}
MPI_Status probe(int source, int tag)
{
    MPI_Status status;
    MPI_Probe(source, tag, MPI_COMM_WORLD, &status);
    return status;
}
template<class T> int get_msg_size(MPI_Status& status)
{
    int num_amnt;
    MPI_Get_count(&status, get_mpi_type<T>::type(), &num_amnt);
    return num_amnt;
}

template<class T> void receive(T* data, int count, int source, int tag, MPI_Status& status = *MPI_STATUS_IGNORE)
{
    MPI_Recv(data, count, get_mpi_type<T>::type(), source, tag, MPI_COMM_WORLD, &status);
}
template<class T> void receive(std::vector<T>& dest, int source, int tag)
{
    MPI_Status status = probe(source, tag);
    int size = get_msg_size<T>(status);
    dest.clear();
    dest.resize(size);
    receive(&dest[0], size, source, tag, status);
}
template<class T> void receive(std::basic_string<T>& dest, int source, int tag)
{
    MPI_Status status = probe(source, tag);
    int size = get_msg_size<T>(status);
    dest.clear();
    dest.resize(size);
    receive(&dest[0], size, source, tag, status);
}
} mpi;