版权声明:博主原创文章属私人所有,未经允许 不可转发和使用 https://blog.csdn.net/a1066196847/article/details/89681871
main.cpp
#include <iostream>
#include <vector>
#include <cstdlib>
#include <time.h>
#include <mpi.h>
using namespace std;
using namespace std;
typedef struct {
int location; //当前在哪个位置
int num_steps_left_in_walk; //要走多少步
} Walker;
// 函数目的:根据传进来的world_rank进程号,然后得到每个星域的起点、每个星域的长度
// 并且初始化到 subdomain_start subdomain_size 中
void decompose_domain(int domain_size, int world_rank,
int world_size, int* subdomain_start,
int* subdomain_size) {
// domain_size是100,world_size不能大于这个
if (world_size > domain_size) {
MPI_Abort(MPI_COMM_WORLD, 1);
}
// domain_size就是整个的长度100
// world_size 是程序启动的总进程数,相当于介绍中的5
// domain_size / world_size是一个"星域"的长度,再乘以world_rank,就是这个星域的起点
*subdomain_start = domain_size / world_size * world_rank;
*subdomain_size = domain_size / world_size;
// 当整个长度不能够平均分的话,就把多余的给最后一个"星域"
if (world_rank == world_size - 1) {
*subdomain_size += domain_size % world_size;
}
}
// 函数目的:每个进程都有num_walkers_per_proc(20)个walker,在这个函数里面初始化,location都是同一个起点
// 但是num_steps_left_in_walk是不同的(随机赋一个值 0~500),因为不管是哪个world_rank都会有20个walkers,
// 所以不用传参数 world_rank
// 然后把这20个walker放到incoming_walkers中
void initialize_walkers(int num_walkers_per_proc, int max_walk_size,
int subdomain_start,
vector<Walker>* incoming_walkers) {
Walker walker;
for (int i = 0; i < num_walkers_per_proc; i++) {
// 首先把当前进程的起点,也就是"星域"的起点,赋值给walker.location
walker.location = subdomain_start;
// 然后生成一个 0~max_walk_size 之间的一个数字,也就是 0~500间的一个数字
// 也就是这个walker将要走的一个步长
walker.num_steps_left_in_walk =
(rand() / (float)RAND_MAX) * max_walk_size;
// 将这个walker放在 存储将来进行的游走队列中
incoming_walkers->push_back(walker);
}
}
// 函数功能:传进来一个walker,然后让这个wakler走到尽头
// subdomain_start: 一个星域的起点
// subdomain_size: 这个星域的长度
// domain_size: 所有星域的总长度
void walk(Walker* walker, int subdomain_start, int subdomain_size,
int domain_size, vector<Walker>* outgoing_walkers) {
// 根据传进来的参数,num_steps_left_in_walk的大小在 0~499,代表意义是这个walker最大走的"步数",一次可以走1
// 刚开始的时候location、subdomain_start大小是一样的,但是walker要往前走才行,走到当前星域的最后1位就可以了
// 假设location和subdomain_start刚开始都是0,第一个星域的大小是4,等到walker走到 0+4=4,也就是当前星域的最后1位
while (walker->num_steps_left_in_walk > 0) {
if (walker->location == subdomain_start + subdomain_size) {
// Take care of the case when the walker is at the end
// of the domain by wrapping it around to the beginning
if (walker->location == domain_size) {
walker->location = 0;
}
// 只有能走到的才会放到outgoing_walkers中,也就是说可能20个wakler只有18个能走到
outgoing_walkers->push_back(*walker);
break;
} else {
// 走1步时,如果不满足 到达 -> 当前星域的最后1位
// 先给能走的步数减1,再给location加1(也就是walker的当前位置)
walker->num_steps_left_in_walk--;
walker->location++;
}
}
}
void send_outgoing_walkers(vector<Walker>* outgoing_walkers,
int world_rank, int world_size) {
// 把这些数据当做 MPI_BYTEs类型的数组,传送给下一个进程。最后一个进程传给0进程
// (void*)outgoing_walkers->data() -> 这些数据要进行传送
// outgoing_walkers->size() * sizeof(Walker) -> 这是数据的大小
// MPI_BYTE -> 数据类型
// (world_rank + 1) % world_size -> 要传给哪个进程
// 0 -> 传送的数据tag
// MPI_COMM_WORLD -> 环境变量
MPI_Send((void*)outgoing_walkers->data(),
outgoing_walkers->size() * sizeof(Walker), MPI_BYTE,
(world_rank + 1) % world_size, 0, MPI_COMM_WORLD);
// 释放内存
outgoing_walkers->clear();
}
void receive_incoming_walkers(vector<Walker>* incoming_walkers,
int world_rank, int world_size) {
MPI_Status status;
// Receive from the process before you. If you are process zero,
// receive from the last process
// incoming_rank -> 当前进程要接送哪个进程传来的数据
// 0 -> 要传来的数据的tag
// MPI_COMM_WORLD -> 环境变量
int incoming_rank =
(world_rank == 0) ? world_size - 1 : world_rank - 1;
MPI_Probe(incoming_rank, 0, MPI_COMM_WORLD, &status);
// Resize your incoming walker buffer based on how much data is
// being received
int incoming_walkers_size;
MPI_Get_count(&status, MPI_BYTE, &incoming_walkers_size);
incoming_walkers->resize(
incoming_walkers_size / sizeof(Walker));
MPI_Recv((void*)incoming_walkers->data(), incoming_walkers_size,
MPI_BYTE, incoming_rank, 0, MPI_COMM_WORLD,
MPI_STATUS_IGNORE);
}
int main(int argc, char** argv) {
int domain_size;
int max_walk_size;
int num_walkers_per_proc;
if (argc < 3) {
cerr << "Usage: random_walk domain_size max_walk_size "
<< "num_walkers_per_proc" << endl;
exit(1);
}
domain_size = atoi(argv[1]); //所有星域的长度 -- 100
max_walk_size = atoi(argv[2]); //一个walker运行的最大步长 -- 500
num_walkers_per_proc = atoi(argv[3]); //每一个进程需要启动多少个walker -- 20
MPI_Init(NULL, NULL);
int world_size;
MPI_Comm_size(MPI_COMM_WORLD, &world_size);
int world_rank;
MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
srand(time(NULL) * world_rank);
int subdomain_start, subdomain_size;
vector<Walker> incoming_walkers, outgoing_walkers;
// 得到"当前world_rank"的星域的起点,星域的长度
decompose_domain(domain_size, world_rank, world_size,
&subdomain_start, &subdomain_size);
// 函数目的:每个进程都有num_walkers_per_proc个walker,在这个函数里面初始化,把当前进程的
// 所有walker放在incoming_walkers中,代表即将到来的walkers
initialize_walkers(num_walkers_per_proc, max_walk_size, subdomain_start,
&incoming_walkers);
// 当前是哪个进程?这个进程有多少个walkers(这都是固定的,在传参中已有)?
// 当前进程的walkers所在区间是什么 subdomain_start ~ (subdomain_start + 星域长度)-1
// 所在区间也就是所在星域的区间
cout << "Process " << world_rank << " initiated " << num_walkers_per_proc
<< " walkers in subdomain " << subdomain_start << " - "
<< subdomain_start + subdomain_size - 1 << endl;
// domain_size / world_size -> 一个星域的长度
// 500 / 20 -> 25 +1 -> 26 可能路过的最大星域的数目
int maximum_sends_recvs = max_walk_size / (domain_size / world_size) + 1;
for (int m = 0; m < maximum_sends_recvs; m++) {
// 处理所有的walkers,让他们走到当前星域的最后1位
for (int i = 0; i < incoming_walkers.size(); i++) {
walk(&incoming_walkers[i], subdomain_start, subdomain_size,
domain_size, &outgoing_walkers);
}
cout << "Process " << world_rank << " sending " << outgoing_walkers.size()
<< " outgoing walkers to process " << (world_rank + 1) % world_size
<< endl;
// 如果是偶数的进程 就先发送再接受
if (world_rank % 2 == 0) {
// Send all outgoing walkers to the next process.
send_outgoing_walkers(&outgoing_walkers, world_rank,
world_size);
// Receive all the new incoming walkers
receive_incoming_walkers(&incoming_walkers, world_rank,
world_size);
} else {
// Receive all the new incoming walkers
receive_incoming_walkers(&incoming_walkers, world_rank,
world_size);
// Send all outgoing walkers to the next process.
send_outgoing_walkers(&outgoing_walkers, world_rank,
world_size);
}
cout << "Process " << world_rank << " received " << incoming_walkers.size()
<< " incoming walkers" << endl;
}
cout << "Process " << world_rank << " done" << endl;
MPI_Finalize();
return 0;
}
执行的时候,第二个参数如果设置的小于每个星域的长度,那么整个程序会很快停止下来,因为每一批wakler从一个星域到下一个星域的时候,能走的最大数目连星域长度都不够,肯定就只能传过去0个,所以很快结束
有兴趣的朋友可以试试这两种参数导致的不同结果
mpicxx main.cpp -o main.out
mpirun -np 5 main.out 100 500 20
mpirun -np 5 main.out 100 5 20
打印出来的完全不一样,也可以利用这个来了解这个程序的逻辑