考试比较难,课程比较繁琐.
高性能计算2022加粗样式考试
1. 启动MPI程序时系统生成的是1维程序···
-
写出一个子程序或函数生成行和列通讯子
思路:
首先进行参数合法性的检查,然后将通信子对应的进程组进行划分,再将通信子对应的进程组进行列划分,最后将通信子中的进程编号使用MPI_Comm_rank进行分发int getComm(int myid, int np, int p, int q, int myrow,int mycol, MPI_Comm *rowComm,MPI_Comm *colComm){if(np < p * q) return 1;int key, color;if(myid < p * q)col = myid / q;elsecolor = MPI_UNDEEFINEDkey = myid;MPI_Comm_split(MPI_COMM_WORLD, color, key, rowComm);if(myid < p*q)color = myid % q;elsecol = MPI_UNDEEFINED;key = myid;MPI_Comm_split(MPI_COMM_WORLD, color,key,colComm);if(myid < p*q){MPI_Comm_rank(colComm, &myrow);MPI_Comm_rank(rowComm, &mycol);}return 0; }
-
卷帘方式计算值
使用卷帘存储方式,即对于一般的m*n 矩阵和一般的处理器阵列p*q,矩阵元素aija_{ij}aij存放在处理器PklP_{kl}Pkl中,其中k=i mod p, l=j mod q。用一个8行8列的矩阵在3*2的处理器阵列进行处理,可知对于aij=astkla_{ij} = a^{kl}_{st}aij=astkl有,k=imodp,l=jmodq,s=i/p,t=j/qk=i\space{mod}\space p, l = j \space mod \space q,s = i/p, t = j/qk=i mod p,l=j mod q,s=i/p,t=j/q
-
广播进程的实现
思路:
现在第0行的行进程中广播p00p_{00}p00上的数据A,此时整行都有该数据,然后再各个列通讯子中广播到整列int broadcastRowCol(int myid, int np, int myrow, int mycol, float *a, int count, MPI_Comm rowComm,MPI_Comm colComm){if(myrow == 0)MPI_Bcast(a, count, MPI_FLOAT, 0, rowComm);MPI_Bcast(a, count, MPI_FLOAT, 0, colComm); }
2. 分块矩阵
-
一次性发送和接收A00A_{00}A00和A20A_{20}A20
思路:
使用MPI_Type_vector 函数构建一个vector 的类型,命名为submat,然后使其extent ,即延伸尺寸正好可以覆盖第一行和第二行矩阵块,然后我们如果使用两个submat 变量,第一个submat 变量传输的内容是A00A_{00}A00 ,但其延伸尺寸覆盖过了第一行其他矩阵块和第二行矩阵块,第二个submat 的起点正好是A20A_{20}A20 ,可以传输A20A_{20}A20 矩阵块。
/* 假设整个矩阵式n*n大小,矩阵按行优先存储,数据类型以float为例子 */ MPI_Datatype submat_base, submat; MPI_Type_vector(m, m, n, MPI_FLOAT, &submat_base); int array_of_blocklens[2] = {1,1}; int array_of_disps[2] = {0,2*m*n*sizeof(float)}; MPI_Datatype array_of_types[2] = {submat_base, MPI_UB}; MPI_Type_struct(2, array_of_blocklens, array_of_disps, array_of_types, &submat); MPI_Type_commit(&submat);
-
只传输A00A_{00}A00和A20A_{20}A20
思路:
以直接利用MPI_Type_indexed 将所需的内容索引到数据类型里就行了。沿用第1 问中的假设条件,先进行赋值块大小和块间隔,然后更正第二个矩阵块的间隔,最后提交结果MPI_Datatype submat; int *array_of_blocklens = new int[2 * m]; int *array_of_disps = new int[2 * m]; for(int i=0; i<2*m; i++){array_of_blocklens[i] = m;array_of_disps[i] = i * n; } for(int i=m; i<2*m; i++)array_of_disps[i] = (i + 2 * m) * n; MPI_Type_indexed(2*m, array_of_blocklens, array_of_disps, MPI_FLOAT, &submat); MPI_Type_commit(&submat);
3. 自定义数据结构的分发
思路:
采用MPI_Type_struct 方法,将需要传输的内容整合成一个MPI 结构体进行传输。首先初始化进程环境,然后获取通信子的进程编号和进程个数,再使用MPI的进行自定义数据类型和打包
#include <stdio.h>
#include <mpi.h>
typedef struct{int m[3];float a[2];char c[5];
}dataType;
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_Datatype newType;int blocks[3] = {3, 2, 5};MPI_Aint disps[3] = {0, 12, 20};MPI_Datatype types[3] = {MPI_INT, MPI_FLOAT, MPI_CHAR};MPI_Type_struct(3, blocks, disps, types, &newType);MPI_Type_commit(&newType);dataType t1 = {{1, 2, 3}, {4.0, 5.0}, {'a', 'b', 'c', 'd', '\0'}};dataType t2;if (rank == 0)MPI_Send(&t1, 1, newType, 1, 0, MPI_COMM_WORLD);else{MPI_Recv(&t2, 1, newType, 0, 0, MPI_COMM_WORLD,MPI_STATUS_IGNORE);printf("%d %d %d %f %f %s\n", t2.m[0], t2.m[1], t2.m[2], t2.a[0], t2.a[1],t2.c);}MPI_Type_free(&newType);MPI_Finalize();
}
4. MPI_Allgather的实现
-
一个如何实现的详细并行方法
思路:
MPI_Alltoall 的功能是全交换,即各个进程都在向所有进程发送对应的分段,同时从所以进程接收发来的分段。这里给出一种简单的实现方式,即每个进程使用一个循环向其他进程发送分段,同时循环中接受其他进程发来的分段,而自己发给自己的部分直接使用memcpy 拷贝 -
MPI_Allgather子程序的实现
int myAlltoall(const void *sendbuf, int sendcount,MPI_Datatype sendtype, void *recvbuf, int recvcount, MPI_Datatype recvtype, MPI_Comm comm) {int rank, size;int type_size;MPI_Type_size(sendtype, &type_size);MPI_Comm_rank(MPI_COMM_WORLD, &rank);MPI_Comm_size(MPI_COMM_WORLD, &size);for (int i = 0; i < size; ++i){if (i == rank){memcpy(sendbuf + i * sendcount * type_size, recvbuf + i * recvcount *type_size, sendcount * type_size);continue;}MPI_Send(sendbuf + i * sendcount * type_size, sendcount, sendtype, i, 0, MPI_COMM_WORLD);MPI_Recv(recvbuf + i * recvcount * type_size, recvcount, recvtype, i, 0,MPI_COMM_WORLD, MPI_STATUS_IGNORE);}return 0; }