为了实现计算与通信的最大重叠 一个通用的原则就是 尽早开始通信 尽晚完成通信
在开始通信和完成通信之间进行计算 这样通信启动得越早 完成得越晚 就有可能有更多
的计算任务可以和通信重叠 也使通信可以在计算任务执行期间完成 而不需要专门的等待
时间
为此 修改Jacobi迭代过程如下
1 计算迭代任务中下次需要通信的数据
2 启动非阻塞通信 传递这些数据 转下一次迭代
3 计算剩余的迭代部分
如果一个通信会被重复执行 比如循环结构内的通信调用 MPI提供了特殊的实现方式 对这样的通信进行优化 以降低不必要的通信开销 它将通信参数和MPI的内部对象建立固 定的联系 然后通过该对象完成重复通信的任务 。大致步骤如下
1 通信的初始化 比如MPI_SEND_INIT
2 启动通信 MPI_START
3 完成通信 MPI_WAIT
4 释放查询对象 MPI_REQUEST_FREE
在开始通信和完成通信之间进行计算 这样通信启动得越早 完成得越晚 就有可能有更多
的计算任务可以和通信重叠 也使通信可以在计算任务执行期间完成 而不需要专门的等待
时间
为此 修改Jacobi迭代过程如下
1 计算迭代任务中下次需要通信的数据
2 启动非阻塞通信 传递这些数据 转下一次迭代
3 计算剩余的迭代部分
4 完成非阻塞通信
#include<mpi.h>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 8;
const int SIZE = N / 4;
const int T = 2;
int main()
{
double matrix1[SIZE+2][N], matrix[SIZE+2][N];
int myid;
MPI_Status status[4];
MPI_Request request[4];
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
memset(matrix1, 0, sizeof matrix1);
memset(matrix2, 0, sizeof matrix2);
//赋值边界数据
if(0 == myid)
for(int j = 0; j < N; j++)
matrix1[1][j] = matrix[2][j] = N;
if(3 == myid)
for(int j = 0; j < N; j++)
matrix1[SIZE][j] = matrix2[SIZE][j] = N;
for(int i = 1; i < SIZE+1; i++)
matrix1[i][0] = matrix1[i][N-1] = matrix2[0] = matrix2[N-1] = N;
//引入虚拟进程简化代码
int up_proc_id = myid==0 ? MPI_PROC_NULL : myid - 1;
int down_proc_id = myid==3 ? MPI_PROC_NULL : myid + 1;
//jacobi迭代
int t, row, col;
for(t = 0; t < T; t++)
{
if(0 == myid)
for(col = 1; col < N-1; col++)
matrix2[SIZE][col] = 0.25 * (matrix1[SIZE][col-1] + matrix1[SIZE][col+1] + matrix1[SIZE-1][col] + matrix[SIZE+1][col]);
else if(3 == myid)
for(col = 1; col < N-1; col++)
matrix2[1][col] = 0.25 * (matrix1[1][col-1] + matrix1[1][col+1] + matrix1[0][col] + matrix1[2][col]);;
else
for(col = 1; col < N-1; col++) // 中间的矩阵块
matrix2[SIZE][col] = 0.25 * (matrix1[SIZE][col-1] + matrix1[SIZE][col+1] + matrix1[SIZE+1][col] + matrix1[SIZE-1][col]);
matrix2[1][col] = 0.25 * (matrix1[1][col-1] + matrix1[1][col+1] + matrix1[2][col] + matrix1[0][col]);
//利用非阻塞通信传递边界数据
int tag1 = 1, tag2 = 2;
MPI_Isend(&matrix2[1][0], N, MPI_DOUBLE, up_proc_id, tag1, MPI_COMM_WORLD, request[0]);
MPI_Isend(&matrix2[SIZE][0], N, MPI_DOUBLE, down_proc_id, tag2, MPI_COMM_WORLD, request[1]);
MPI_Irecv(&matrix1[SIZE+1][0], N, MPI_DOUBLE, down_proc_id, tag1, MPI_COMM_WORLD, request[2]);
MPI_Irecv(&matrix1[0][0], N, MPI_DOUBLE, up_proc_id, tag1, MPI_COMM_WORLD, request[3]);
//利用通信的时间计算矩阵中间值
int begin_row = 0==myid ? 2 : 1;
int end_row = 3==myid ? (SIZE-1) : (SIZE);
for (row = begin_row; row < end_row; row++)
for (col = 1; col < N-1; col++)
matrix2[row][col] = 0.25 * (matrix1[row][col-1] + matrix1[row][col+1] + matrix1[row+1][col] + matrix1[row-1][col]);
//利用通信的时间更新矩阵
for(row = begin_row; row <= end_row; row++)
for(col = 1; col < N-1; col++)
matrix1[row][col] = matrix2[row][col];
MPI_Waitall(4, &request[0], &status[0]);
}
MPI_Barrier(MPI_COMM_WORLD);
MPI_Finalize();
return 0;
}如果一个通信会被重复执行 比如循环结构内的通信调用 MPI提供了特殊的实现方式 对这样的通信进行优化 以降低不必要的通信开销 它将通信参数和MPI的内部对象建立固 定的联系 然后通过该对象完成重复通信的任务 。大致步骤如下
1 通信的初始化 比如MPI_SEND_INIT
2 启动通信 MPI_START
3 完成通信 MPI_WAIT
4 释放查询对象 MPI_REQUEST_FREE
#include<mpi.h>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 8;
const int SIZE = N / 4;
const int T = 2;
int main()
{
double matrix1[SIZE+2][N], matrix[SIZE+2][N];
int myid;
MPI_Status status[4];
MPI_Request request[4];
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
memset(matrix1, 0, sizeof matrix1);
memset(matrix2, 0, sizeof matrix2);
//赋值边界数据
if(0 == myid)
for(int j = 0; j < N; j++)
matrix1[1][j] = matrix[2][j] = N;
if(3 == myid)
for(int j = 0; j < N; j++)
matrix1[SIZE][j] = matrix2[SIZE][j] = N;
for(int i = 1; i < SIZE+1; i++)
matrix1[i][0] = matrix1[i][N-1] = matrix2[0] = matrix2[N-1] = N;
//引入虚拟进程简化代码
int up_proc_id = myid==0 ? MPI_PROC_NULL : myid - 1;
int down_proc_id = myid==3 ? MPI_PROC_NULL : myid + 1;
//初始化重复非阻塞通信
int tag1 = 1, tag2 = 2;
MPI_Send_init(&matrix2[1][0], N, MPI_DOUBLE, up_proc_id, tag1, MPI_COMM_WORLD, request[0]);
MPI_Send_init(&matrix2[SIZE][0], N, MPI_DOUBLE, down_proc_id, tag2, MPI_COMM_WORLD, request[1]);
MPI_Recv_init(&matrix1[SIZE+1][0], N, MPI_DOUBLE, down_proc_id, tag1, MPI_COMM_WORLD, request[2]);
MPI_Recv_init(&matrix1[0][0], N, MPI_DOUBLE, up_proc_id, tag1, MPI_COMM_WORLD, request[3]);
//jacobi迭代
int t, row, col;
for(t = 0; t < T; t++)
{
if(0 == myid)
for(col = 1; col < N-1; col++)
matrix2[SIZE][col] = 0.25 * (matrix1[SIZE][col-1] + matrix1[SIZE][col+1] + matrix1[SIZE-1][col] + matrix[SIZE+1][col]);
else if(3 == myid)
for(col = 1; col < N-1; col++)
matrix2[1][col] = 0.25 * (matrix1[1][col-1] + matrix1[1][col+1] + matrix1[0][col] + matrix1[2][col]);;
else
for(col = 1; col < N-1; col++) // 中间的矩阵块
matrix2[SIZE][col] = 0.25 * (matrix1[SIZE][col-1] + matrix1[SIZE][col+1] + matrix1[SIZE+1][col] + matrix1[SIZE-1][col]);
matrix2[1][col] = 0.25 * (matrix1[1][col-1] + matrix1[1][col+1] + matrix1[2][col] + matrix1[0][col]);
//启动非阻塞通信传递边界数据
MPI_Startall(4, &request[0]);
//利用通信的时间计算矩阵中间值
int begin_row = 0==myid ? 2 : 1;
int end_row = 3==myid ? (SIZE-1) : (SIZE);
for (row = begin_row; row < end_row; row++)
for (col = 1; col < N-1; col++)
matrix2[row][col] = 0.25 * (matrix1[row][col-1] + matrix1[row][col+1] + matrix1[row+1][col] + matrix1[row-1][col]);
//利用通信的时间更新矩阵
for(row = begin_row; row <= end_row; row++)
for(col = 1; col < N-1; col++)
matrix1[row][col] = matrix2[row][col];
MPI_Waitall(4, &request[0], &status[0]);
}
//释放非阻塞通信对象
for(int i = 0; i < 4; i++) MPI_Request_free(&request[i]);
MPI_Barrier(MPI_COMM_WORLD);
MPI_Finalize();
return 0;
}
本文深入探讨了MPI中的Jacobi迭代法,并重点讲解了非阻塞通信的概念及其在迭代过程中的应用,揭示了如何利用非阻塞通信提升并行计算效率。
&spm=1001.2101.3001.5002&articleId=79169731&d=1&t=3&u=b531500c005f42e39f1468a492827517)

被折叠的 条评论
为什么被折叠?



