VS2012+Microsoft HPC Pack下MPI任务间变量共享实现方法咨询
MPI并行程序实现任务间共享递增变量输出1 2 3 4
问题背景
我正在使用C++结合MPI,在Visual Studio 2012与Microsoft HPC Pack环境下开发并行程序。运行以下代码:
#include <iostream> #include "mpi.h" using namespace std; void main(int argc, char * argv[]){ MPI_Init(&argc, &argv); int i = 0; i++; cout << "i = " << i << endl; MPI_Finalize(); }
通过cmd执行命令mpiexec App.exe后,输出结果为1 1 1 1。请问如何实现变量i在所有任务间共享,让每个任务依次递增i的值,使输出变为1 2 3 4?
解决方案
为什么原代码输出全是1?
首先得明确:MPI的每个进程都拥有独立的内存空间,你代码里的变量i是每个进程的局部变量。每个进程都会执行i=0; i++;,所以每个进程的i最终都是1,输出自然全是1。要实现递增的输出,我们需要通过MPI的通信机制来协调进程间的数据分配或共享。
方案一:用MPI_Scatter分发预定义的递增值(简单直接)
这种方法适合你明确知道进程数量的场景,根进程(比如0号进程)生成一个包含1到N的数组,然后把数组元素逐个分发给每个进程,每个进程拿到对应的数值后输出即可。
修改后的代码如下:
#include <iostream> #include "mpi.h" using namespace std; int main(int argc, char * argv[]) { MPI_Init(&argc, &argv); int rank, size; MPI_Comm_rank(MPI_COMM_WORLD, &rank); // 获取当前进程编号(从0开始) MPI_Comm_size(MPI_COMM_WORLD, &size); // 获取总进程数 int i = 0; if (rank == 0) { // 根进程生成要分发的数组:1,2,...,size int* values = new int[size]; for (int j = 0; j < size; j++) { values[j] = j + 1; } // 将数组元素分发给每个进程,每个进程拿到对应位置的元素 MPI_Scatter(values, 1, MPI_INT, &i, 1, MPI_INT, 0, MPI_COMM_WORLD); delete[] values; } else { // 非根进程接收分发的值 MPI_Scatter(NULL, 1, MPI_INT, &i, 1, MPI_INT, 0, MPI_COMM_WORLD); } // 输出当前进程的i值 cout << "i = " << i << endl; MPI_Finalize(); return 0; }
代码说明:
MPI_Comm_rank获取当前进程的编号(0到size-1),MPI_Comm_size获取总进程数;- 根进程(rank=0)生成包含1到size的数组;
MPI_Scatter将数组的每个元素分发给对应编号的进程,每个进程的i就会被设置为1、2、3、4(当总进程数为4时);- 最后每个进程输出自己的
i值,就能得到你想要的1 2 3 4(注:输出顺序可能因进程调度略有不同,但数值是正确的,如果需要严格按顺序输出,可以再用MPI_Gather收集后统一输出)。
方案二:用MPI原子操作实现动态共享计数器(更灵活)
如果需要动态地让每个进程依次获取递增的计数器值(比如不确定进程启动顺序的场景),可以用MPI的原子操作MPI_Fetch_and_add来实现共享计数器。这种方法需要创建一个MPI窗口来共享内存。
修改后的代码如下:
#include <iostream> #include "mpi.h" using namespace std; int main(int argc, char * argv[]) { MPI_Init(&argc, &argv); int rank, size; MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &size); MPI_Win win; int counter = 0; // 创建共享内存窗口:只有根进程持有counter变量,其他进程可以访问 MPI_Win_create(&counter, sizeof(int), sizeof(int), MPI_INFO_NULL, MPI_COMM_WORLD, &win); MPI_Win_fence(0, win); // 原子操作:获取当前counter的值,然后将counter加1 int i; MPI_Fetch_and_add(&counter, 1, &i, MPI_INT, 0, win); i++; // 因为初始counter是0,所以第一个进程拿到0,加1后是1,以此类推 MPI_Win_fence(0, win); MPI_Win_free(&win); cout << "i = " << i << endl; MPI_Finalize(); return 0; }
代码说明:
MPI_Win_create创建一个共享内存窗口,根进程(0号)的counter变量被共享;MPI_Fetch_and_add是原子操作,保证每个进程都会依次获取当前的counter值,然后原子地将counter加1,避免多个进程同时修改导致的竞争;- 因为初始
counter是0,所以每个进程拿到的i需要加1才能得到1、2、3、4; MPI_Win_fence用于同步进程,确保内存操作的顺序性。
执行注意事项
在Visual Studio 2012和Microsoft HPC Pack环境下,确保你的项目正确配置了MPI的包含目录和库目录,执行时用mpiexec -n 4 App.exe指定4个进程(如果需要更多进程,调整数字即可)。
内容的提问来源于stack exchange,提问作者Abdul Rahman Al Hourani
相关产品推荐
相关产品推荐

