点对点通讯应用-随机游走问题描述
问题的基本定义如下,给定一个 Min, Max, 和随机游走器 W,使得 W 在往右走 S 步。如果超出范围,则会重新回到起点。W 每次只能移动一个单位。
让我们概述一下如何并行处理随机游走。
与许多并行处理程序一样,第一项相关任务是在各个进程之间划分域。随机游走问题具有大小为 Max - Min + 1 的一维域。假设 Walker 只能走整数大小的步数,我们可以轻松地在整个过程中将域划分为几乎相等的块。
例如,如果 Min 为 0,Max 为 20,我们有 4 个进程,我们可以进行如下域划分。
前三个进程每个进程拥有域的 5 个单元,最后一个进程占用 6 个单元。一旦对域进行了划分,应用程序将初始化 Walker 。
例如,进程 0 进行 6 步游走,执行步骤如下:
- Walker 开始采取增量步骤。但是当其达到值 4 时,它已到达进程 0 的边界的尽头。现在,进程 0 必须跟进程 1 进行通讯。
- 进程 1 接收 Walker ,并继续行走达到总步数 6 。然后 Walker 可以继续进行新的随机游走。
如果步长更长, Walker 有可能经过更多进程。
编码解决
首先我们确定程序的基本特征:
- 每个进程处理域的一部分
- 每个进程初始化 N 次 Walker ,每次遍历在本地域的第一个值开始
- 每个 Walker 有两个相关整数值,当前 Walker 的位置以及剩余要走的步数
- Walker 遍历该域,并传递到下一个进程,直到完成游走
- 当所有 Walker 都完成时,所有进程终止
首先我们编写域分解的代码,该函数将考虑域的总大小并未 MPI 进程找到合适的子域,并将域的其余部分交给最终进程。为了简便,我们调用 MPI_Abort
处理任何发现的错误。
void decompose_domain(int domain_size, int world_rank,
int world_size, int* subdomain_start,
int* subdomain_size) {
if (world_size > domain_size) {
// 不关心此特例,假设域的大小一点过大于通讯组规模
MPI_Abort(MPI_COMM_WORLD, 1);
}
*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;
}
}
该函数返回一个子域开始地址以及子域大小。
我们定义一个 Walker 结构:
typedef struct {
int location;
int num_steps_left_in_walk;
} Walker;
定义初始化 Walker 函数,接受子域边界并添加游走器进入 incoming_walkers
向量中。
void initialize_walkers(int num_walkers_per_proc, int max_walk_size,
int subdomain_start, int subdomain_size,
vector<Walker>* incoming_walkers) {
Walker walker;
for (int i = 0; i < num_walkers_per_proc; i++) {
// 在子域中初始化 Walker
walker.location = subdomain_start;
walker.num_steps_left_in_walk =
(rand() / (float)RAND_MAX) * max_walk_size;
incoming_walkers->push_back(walker);
}
}
初始化之后就该使得 Walker 前进了,我们构造一个函数负责行走功能,并将超出本地范围内的步行器加入到 outgoing_walkers
向量中。
void walk(Walker*