#include <stdio.h>
#include <mpi.h>
#include <unistd.h>
#include <stdlib.h> #define S 4 //分块方阵的大小
#define RB 8 //行方向分块维数
#define B RB*RB //并行进程数
#define N S*RB //方阵行列数
#define BS S+2 //块包含交换数据的方阵大小
#define T 2 //迭代次数 //并行-重复非阻塞-笛卡尔64个进程块
/*
优化:通信接口分步优化 MPI_Start
要求:
1、仍然采取二维行列同时分块的方式,对数据进行区域分解。
2、使用笛卡尔虚拟拓扑的相关接口,进行二维进程网格阵列构建、邻居进程编号获取等操作。
3、使用64个进程,每个进程初始化一个二维子块,并负责该子块的Jacobi迭代计算。
4、使用重复非阻塞通信接口。
思路:使用虚拟进程和笛卡尔,每个进程都对上下左右块通信
*/
void printRows(int pid,float rows[BS][BS])
{
printf("result in %d\n",pid);
for(int i=0;i<BS;i++)
{
for(int j=0;j<BS;j++)
printf("%.3f\t",rows[i][j]);
printf("\n");
}
} void RequestStart(int count,MPI_Request arr_request[])
{
for(int i=0;i<count;i++)
MPI_Start(&arr_request[i]);
} void RequestFree(int count,MPI_Request arr_request[])
{
for(int i=0;i<count;i++)
MPI_Request_free(&arr_request[i]);
} int main(int argc,char* argv[])
{
float rows[BS][BS],rows2[BS][BS],temprows[S][S],temprows1[N][N],finalrows[N][N]; //int top=0,bottom=0,left=0,right=0; //标记每个block实际数据的边界
//int ltBID=0,rtBID=1,lbBID=2,rbBID=3;//标记四个角落位置的进程
MPI_Status arr_status[2+S*2]={0}; //
MPI_Request* arr_requestS = calloc(2+S*2,sizeof(MPI_Request)); //发送请求
MPI_Request* arr_requestR = calloc(2+S*2,sizeof(MPI_Request)); //接收请求
int dims[2]={N/S,N/S}; //8,8
int periods[2] = {0,0}; //每个维度中不定期
MPI_Comm cartcomm;
int pid; //当前进程ID
int size=0;
int coords[2] ={0}; //当前进程笛卡尔坐标
int nbrs[4] ={0}; //上下左右进程的id
enum DIR{UP,DOWN,LEFT,RIGHT}; MPI_Init(&argc,&argv);
MPI_Comm_size(MPI_COMM_WORLD,&size);
MPI_Comm_rank(MPI_COMM_WORLD,&pid);
if(size == B)
{
MPI_Cart_create(MPI_COMM_WORLD,2,dims,periods,0,&cartcomm);
MPI_Cart_coords(cartcomm,pid,2,coords); //初始化
for(int i=0; i<BS; i++)
{
for(int j=0; j<BS; j++)
{
rows[i][j] = 0.0;
rows2[i][j] = 0.0;
}
}
//确定上下左右进程的id
MPI_Cart_shift(cartcomm,0,1,&nbrs[UP],&nbrs[DOWN]);
MPI_Cart_shift(cartcomm,1,1,&nbrs[LEFT],&nbrs[RIGHT]);
//有效数据边界初始化
if(pid%RB == 0)//left
{
for(int i=1;i<BS-1;i++)
rows[i][1] = 8.0;
}
if(pid%RB==RB-1)//right
{
for(int i=1;i<BS-1;i++)
rows[i][BS-2] = 8.0;
}
if(pid>=0 && pid<RB)//top
{
for(int i=1;i<BS-1;i++)
rows[1][i] = 8.0;
}
if(pid<B && pid>=B-RB)//bottom
{
for(int i=1;i<BS-1;i++)
rows[BS-2][i] = 8.0;
} //建立通信连接
for(int i=0;i<4;i++)
{
if(nbrs[i] == -1)
nbrs[i] = MPI_PROC_NULL;
}
//上下左右
MPI_Recv_init(&rows[0][1],S,MPI_FLOAT,nbrs[UP],0,cartcomm,&arr_requestR[0]);
MPI_Recv_init(&rows[BS-1][1],S,MPI_FLOAT,nbrs[DOWN],0,cartcomm,&arr_requestR[1]);
for(int i=1,k=2;i<BS-1;i++,k+=2)
{
MPI_Recv_init(&rows[i][0],1,MPI_FLOAT,nbrs[LEFT],0,cartcomm,&arr_requestR[k]);
MPI_Recv_init(&rows[i][BS-1],1,MPI_FLOAT,nbrs[RIGHT],0,cartcomm,&arr_requestR[k+1]);
} MPI_Send_init(&rows[1][1],S,MPI_FLOAT,nbrs[UP],0,cartcomm,&arr_requestS[0]);
MPI_Send_init(&rows[BS-2][1],S,MPI_FLOAT,nbrs[DOWN],0,cartcomm,&arr_requestS[1]);
for(int i=1,k=2;i<BS-1;i++,k+=2)
{
MPI_Send_init(&rows[i][1],1,MPI_FLOAT,nbrs[LEFT],0,cartcomm,&arr_requestS[k]);
MPI_Send_init(&rows[i][BS-2],1,MPI_FLOAT,nbrs[RIGHT],0,cartcomm,&arr_requestS[k+1]);
} //块内需要计算数据的边界索引
int rbegin,rend; //块内起始 终止列号
int cbegin,cend; //块内列起始 终止列号
rbegin = (pid>=0 && pid<RB)?2:1;
rend = (pid<B && pid>=B-RB)?BS-3:BS-2;
cbegin =(pid%RB == 0)?2:1;
cend = (pid%RB == RB-1)?BS-3:BS-2; //迭代
for(int step=0; step<T; step++)
{
//每个进程都完成收发数据才能计算
RequestStart(2+S*2,arr_requestR);
RequestStart(2+S*2,arr_requestS);
MPI_Waitall(2+S*2,arr_requestR,arr_status);
MPI_Waitall(2+S*2,arr_requestS,arr_status); //计算
for(int i=rbegin;i<=rend;i++)
{
for(int j=cbegin;j<=cend;j++)
rows2[i][j] =0.25*(rows[i-1][j]+rows[i][j-1]+rows[i][j+1]+rows[i+1][j]);
}
//更新
for(int i=rbegin;i<=rend;i++)
{
for(int j=cbegin;j<=cend;j++)
rows[i][j] = rows2[i][j];
}
}
//打印
sleep(pid);
printRows(pid,rows); //Gather data from all processes
for(int i=1,m=0;i<BS-1;i++,m++)
{
for(int j=1,n=0;j<BS-1;j++,n++)
temprows[m][n] = rows[i][j];
}
MPI_Barrier(MPI_COMM_WORLD);
MPI_Gather(temprows,S*S,MPI_FLOAT,temprows1,S*S,MPI_FLOAT,0,MPI_COMM_WORLD); //对数据重新整理
//遍历temprows1
int index=0;
for(int rb=0;rb<RB;rb++)//块行索引
{
for(int cb=0;cb<RB;cb++)//块列索引
{
for(int r=0;r<S;r++)
{
for(int c=0;c<S;c++)
{
finalrows[rb*S+r][cb*S+c] = *((float*)&temprows1+index++);
}
}
}
}
if(pid==0)
{
fprintf(stderr,"\nResult after gathering data:\n");
for(int i = 0; i < N; i++)
{
for(int j = 0; j < N; j++)
fprintf(stderr,"%.3f\t", finalrows[i][j]);
fprintf(stderr,"\n");
}
fprintf(stderr,"\n");
}
RequestFree(2+S*2,arr_requestR);
RequestFree(2+S*2,arr_requestS);
}
else if(pid==0)
{
printf("parameter:should -n %d\n",B);
}
free(arr_requestS);
free(arr_requestR);
MPI_Finalize();
return 0;
}

高性能计算-雅可比算法-MPI重复非阻塞优化(7)的更多相关文章

  1. 【MPI学习4】MPI并行程序设计模式:非阻塞通信MPI程序设计

    这一章讲了MPI非阻塞通信的原理和一些函数接口,最后再用非阻塞通信方式实现Jacobi迭代,记录学习中的一些知识. (1)阻塞通信与非阻塞通信 阻塞通信调用时,整个程序只能执行通信相关的内容,而无法执 ...

  2. Java锁与非阻塞算法的性能比较与分析+原子变量类的应用

    15.原子变量与非阻塞同步机制 在java.util.concurrent包中的许多类,比如Semaphore和ConcurrentLinkedQueue,都提供了比使用Synchronized更好的 ...

  3. java并发之非阻塞算法介绍

    在并发上下文中,非阻塞算法是一种允许线程在阻塞其他线程的情况下访问共享状态的算法.在绝大多数项目中,在算法中如果一个线程的挂起没有导致其它的线程挂起,我们就说这个算法是非阻塞的. 为了更好的理解阻塞算 ...

  4. 《Java并发编程实战》笔记-非阻塞算法

    如果在某种算法中,一个线程的失败或挂起不会导致其他线程也失败和挂起,那么这种算法就被称为非阻塞算法.如果在算法的每个步骤中都存在某个线程能够执行下去,那么这种算法也被称为无锁(Lock-Free)算法 ...

  5. 29、Java并发性和多线程-非阻塞算法

    以下内容转自http://ifeve.com/non-blocking-algorithms/: 在并发上下文中,非阻塞算法是一种允许线程在阻塞其他线程的情况下访问共享状态的算法.在绝大多数项目中,在 ...

  6. java并发编程(8)原子变量和非阻塞的同步机制

    原子变量和非阻塞的同步机制 一.锁的劣势 1.在多线程下:锁的挂起和恢复等过程存在着很大的开销(及时现代的jvm会判断何时使用挂起,何时自旋等待) 2.volatile:轻量级别的同步机制,但是不能用 ...

  7. 非阻塞同步机制与CAS操作

    锁的劣势 Java在JDK1.5之前都是靠synchronized关键字保证同步的,这种通过使用一致的锁定协议来协调对共享状态的访问,可以确保无论哪个线程 持有守护变量的锁,都采用独占的方式来访问这些 ...

  8. Java并发编程实战 第15章 原子变量和非阻塞同步机制

    非阻塞的同步机制 简单的说,那就是又要实现同步,又不使用锁. 与基于锁的方案相比,非阻塞算法的实现要麻烦的多,但是它的可伸缩性和活跃性上拥有巨大的优势. 实现非阻塞算法的常见方法就是使用volatil ...

  9. 非阻塞同步算法与CAS(Compare and Swap)无锁算法

    锁(lock)的代价 锁是用来做并发最简单的方式,当然其代价也是最高的.内核态的锁的时候需要操作系统进行一次上下文切换,加锁.释放锁会导致比较多的上下文切换和调度延时,等待锁的线程会被挂起直至锁释放. ...

  10. 【Java并发编程】9、非阻塞同步算法与CAS(Compare and Swap)无锁算法

    转自:http://www.cnblogs.com/Mainz/p/3546347.html?utm_source=tuicool&utm_medium=referral 锁(lock)的代价 ...

随机推荐

  1. Linux 常见编辑器

    命令行编辑器 Vim Linux 上最出名的编辑器当属 Vim 了.Vim 由 Vi 发展而来,Vim 的名字意指 Vi IMproved,表示 Vi 的升级版.Vim 对于新手来说使用比较复杂,不过 ...

  2. Gluon 编译 JavaFx -> exe

    Gluon 编译 JavaFx -> exe 能力强的伙伴可以直接参考官方文档 开发工具 idea 2023.3 idea gluon plugin git apache-maven-3.8.4 ...

  3. Could not resolve placeholder 'xxx.xxx.xxx' in value "http://${xxx.xxx.xxx}"

    代码一切正常,忽然报这个错误, 原因为,当前配置在配置文件最后,且前面均为注释,把当前配置位置提前即可

  4. compileSdkVersion, minSdkVersion 和 targetSdkVersion,傻傻分不清楚【转】

    原文 https://blog.csdn.net/gaolh89/article/details/79809034 在Android Studio项目的app/build.gradle中,我们可以看到 ...

  5. 【YashanDB知识库】stmt未close,导致YAS-00103 no free block in sql main pool part 0报错分析

    问题现象 问题单:YAS-00103 no free block in sql main pool part 0,YAS-00105 out of memory to allocate hash ta ...

  6. MVCC能否解决幻读?

    一.什么是MVCC 多版本控制: 指的是一种提高并发的技术.最早的数据库系统,只有读读之间可以并发,读写,写读,写写都要阻塞.引入多版本之后,只有写写之间相互阻塞,其他三种操作都可以并行,这样大幅度提 ...

  7. CSS – Grid

    前言 有一种布局方式叫 Layout Grid 网格布局. 在 Figma – Layout Grid 有介绍过. 在 RWD 概念篇 也有讲到过 要实现这种布局, 可以用 Flex 也可以用 Gri ...

  8. SpringBoot项目使用yml文件链接数据库异常

    SpringBoot使用properties连接数据库时没有出现问题 SpringBoot使用yml连接数据库时出现:Unable to connect to Redis 并在报错信息中出现: 发现是 ...

  9. 模板声明的两种形式:template<int N> 和 template<N>

    template<int N>:非类型模板参数 template<int N> 是一个典型的非类型模板参数的例子.这里的 N 是模板的参数,但它不是一个类型,而是一个具体的整数 ...

  10. 内核模块踩内存问题定位利器- hardware breakpoint

    内核由于共享内存地址空间,如果没有合适的工具,很多踩内存的问题即使复现,也无法快速定位: 在新的内核版本中引入了一个新工具hardware breakpoint,其能够监视对指定的地址的特定类型(读/ ...