MPI 并行奇偶交换排序 + 集合通信函数 Sendrecv() Sendvecv_replace()
▶ 《并行程序设计导论》第三章的例子程序
● 代码
#include <stdio.h>
#include <mpi.h>
#include <stdlib.h> const int nProcess = , localSize = * , globalSize = nProcess * localSize; int compare(const void *a, const void *b) { return *(int *)a - *(int *)b; }// 用于快排的回调函数 void merge(int *a, const int *b, const bool low)// 归并数组 a 和 b,由 low 决定归并小端还是大端
{
int temp[localSize], pa, pb, ptemp;
if (low)
{
for (pa = pb = ptemp = ; ptemp < localSize;)
{
if (a[pa] <= b[pb])
temp[ptemp++] = a[pa++];
else
temp[ptemp++] = b[pb++];
}
}
else
{
for (pa = pb = ptemp = localSize - ; ptemp >= ;)
{
if (a[pa] <= b[pb])
temp[ptemp--] = b[pb--];
else
temp[ptemp--] = a[pa--];
}
}
for (pa = ptemp = ; pa < localSize; a[pa++] = temp[ptemp++]);
return;
} int main(int argc, char* argv[])
{ int globalData[globalSize], globalDataForQSort[globalSize], localData[localSize], changeData[localSize];
int i, comSize, comRank, partner;
double timeQSort, timeProcess, timeSort, accerlateRatio; MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &comSize);
MPI_Comm_rank(MPI_COMM_WORLD, &comRank); srand();
if (comRank == )
{
printf("Lrngth: %d\n", globalSize);
for (i = ; i < globalSize; globalData[i] = globalDataForQSort[i] = rand(), i++); // CPU 单线程排序并计时
timeQSort = MPI_Wtime();
qsort(globalDataForQSort, globalSize, sizeof(int), &compare);
timeQSort = MPI_Wtime() - timeQSort;
printf("QSort time: %f ms\n", timeQSort * );
} // MPI 多进程排序
MPI_Barrier(MPI_COMM_WORLD);
timeProcess = MPI_Wtime();
MPI_Scatter(globalData, localSize, MPI_INT, localData, localSize, MPI_INT, , MPI_COMM_WORLD);// 分发数据
qsort(localData, localSize, sizeof(int), &compare); // 自行快排
for (i = ; i < nProcess; i++) // 每次循环完成一次交换,交换 nProcess 次完成排序
{
// 寻找 partner
if (i % )
partner = comRank + (comRank % ? + : -);
else
partner = comRank + (comRank % ? - : +);
if (partner == - || partner == comSize)
partner = MPI_PROC_NULL; // 与 partner 交换数据,使用函数 MPI_Sendrecv() 与后面注释中的发送、接收函数等价
MPI_Sendrecv(localData, localSize, MPI_INT, partner, , changeData, localSize, MPI_INT, partner, , MPI_COMM_WORLD, MPI_STATUS_IGNORE);
/*
if (comRank % 2) // 奇数号进程先接受再发送,偶数号进程先发送再接收
{
MPI_Recv(changeData, localSize, MPI_INT, partner, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
MPI_Send(localData, localSize, MPI_INT, partner, 0, MPI_COMM_WORLD);
}
else
{
MPI_Send(localData, localSize, MPI_INT, partner, 0, MPI_COMM_WORLD);
MPI_Recv(changeData, localSize, MPI_INT, partner, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
}
*/
if (partner != MPI_PROC_NULL) // 如果 partner 比本结点大,则本结点保留较小的部分
merge(localData, changeData, partner > comRank);
MPI_Barrier(MPI_COMM_WORLD); // 归并后进行同步,准备下一次交换
}
MPI_Gather(localData, localSize, MPI_INT, globalData, localSize, MPI_INT, , MPI_COMM_WORLD);// 聚集数据到主进程,以便检查结果
timeProcess = MPI_Wtime() - timeProcess;
MPI_Reduce((void *)&timeProcess, (void *)&timeSort, , MPI_DOUBLE, MPI_MAX, , MPI_COMM_WORLD); // 将排序结果与 CPU 单线程的结果相比较
if (comRank == )
{
printf("Sort time: %f ms, accerlate ratio = %f\n", timeSort * , timeQSort / timeSort);
for (i = ; i < globalSize; i++)
{
if (globalData[i] != globalDataForQSort[i])
break;
}
if (i == globalSize)
printf("Result Correct!");
else
printf("Error at i = %d, qsort[i] = %d, sort[i] = %d", i, globalDataForQSort[i], globalData[i]);
}
MPI_Finalize();
return ;
}
● 输出结果。中等规模的数组加速比较高,小规模情形非交换或归并的其他过程耗时较多,没有体现出并行的优势;大规模情形数据交换量较大,耗时较多
D:\Code\MPI\MPIProjectTemp\x64\Debug>mpiexec -n -l MPIProjectTemp.exe
[]Lrngth:
[]QSort time: 2.573407 ms
[]Sort time: 0.785797 ms, accerlate ratio = 3.274899
[]Result Correct!
D:\Code\MPI\MPIProjectTemp\x64\Debug>mpiexec -n -l MPIProjectTemp.exe
[]Lrngth:
[]QSort time: 101.129885 ms
[]Sort time: 20.440968 ms, accerlate ratio = 4.947412
[]Result Correct!
D:\Code\MPI\MPIProjectTemp\x64\Debug>mpiexec -n -l MPIProjectTemp.exe
[]Lrngth:
[]QSort time: 3051.212378 ms
[]Sort time: 687.252457 ms, accerlate ratio = 4.439726
[]Result Correct!
● 在 MPI 代码结尾处混入 getchar(); 会导致进程挂起,无法正常退出,报错代码是 0xc00000fd(栈溢出?),写的时候不要加上这条语句。
▶ 函数 MPI_Sendrecv() 和 MPI_Sendrecv_replace(),同时进行一次阻塞时信息发送和接收,完全由 MPI 进行调度,当发送数据和接收数据变量名不同时使用函数 MPI_Sendrecv(),名字相同时两者均可使用(在函数 MPI_Sendrecv() 中将参数 send_buf_p 与 recv_buf_p 设为相同即可)
● 函数声明
MPI_METHOD MPI_Sendrecv(
_In_opt_ const void* sendbuf, // 发送数据
_In_range_(>= , ) int sendcount, // 发送数据的个数
_In_ MPI_Datatype sendtype, // 发送数据类型
_In_range_(>= , MPI_PROC_NULL) int dest, // 发送目标进程号
_In_range_(>= , ) int sendtag, // 发送标签
_Out_opt_ void* recvbuf, // 接收数据
_In_range_(>= , ) int recvcount, // 接收数据个数
_In_ MPI_Datatype recvtype, // 接收数据类型
_In_range_(>= , MPI_ANY_SOURCE) int source, // 接收数据源
_In_range_(>= , MPI_ANY_TAG) int recvtag, // 接受标签
_In_ MPI_Comm comm, // 通信子
_Out_ MPI_Status* status // 状态结构指针
); MPI_METHOD MPI_Sendrecv_replace(
_Inout_opt_ void* buf, // 合并 sendbuf 和 recvbuf
_In_range_(>= , ) int count, // 合并 sendcount 和 recvcount
_In_ MPI_Datatype datatype, // 合并 sendtype 和 recvtype
_In_range_(>= , MPI_PROC_NULL) int dest,
_In_range_(>= , ) int sendtag,
_In_range_(>= , MPI_ANY_SOURCE) int source,
_In_range_(>= , MPI_ANY_TAG) int recvtag,
_In_ MPI_Comm comm,
_Out_ MPI_Status* status
);
● 函数 MPI_Sendrecv_replace() 的使用范例
{
const int nProcess = , localSize = , globalSize = localSize * nProcess;
int globalData[globalSize], localData[localSize];
int comRank, comSize, i; MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &comRank);
MPI_Comm_size(MPI_COMM_WORLD, &comSize); if (comRank == )
for (i = ; i < globalSize; globalData[i] = i, i++); MPI_Scatter(globalData, localSize, MPI_INT, localData, localSize, MPI_INT, , MPI_COMM_WORLD);
for (i = ; i < localSize; i++) // 打印各进程数据
printf("%2d, ", localData[i]); MPI_Barrier(MPI_COMM_WORLD); // 同步后通信,将每个进程的数据环形发送给下一个进程
MPI_Sendrecv_replace(localData, localSize, MPI_INT, (comRank + ) % nProcess, , (comRank + nProcess - ) % nProcess, , MPI_COMM_WORLD, MPI_STATUS_IGNORE);
MPI_Barrier(MPI_COMM_WORLD); for (i = ; i < localSize; i++) // 再次打印各进程数据
printf("%2d, ", localData[i]); MPI_Finalize();
return ;
}
● 输出结果,发现数据被正确的轮换了,但是各进程的两次输出却是连在一起的,猜想各进程输出时被加载到了同一个缓冲区中,再按照进程排序合并,最后一次性向 stdout 输出,可以使用 fflush(stdout); 来清空缓冲区
D:\Code\MPI\MPIProjectTemp\x64\Debug>mpiexec -n -l MPIProjectTemp.exe
[] , , , , , , , , , , , , , , , ,
[] , , , , , , , , , , , , , , , ,
[], , , , , , , , , , , , , , , ,
[], , , , , , , , , , , , , , , ,
[], , , , , , , , , , , , , , , ,
[], , , , , , , , , , , , , , , ,
[], , , , , , , , , , , , , , , ,
[], , , , , , , , , , , , , , , ,
MPI 并行奇偶交换排序 + 集合通信函数 Sendrecv() Sendvecv_replace()的更多相关文章
- MPI 集合通信函数 MPI_Reduce(),MPI_Allreduce(),MPI_Bcast(),MPI_Scatter(),MPI_Gather(),MPI_Allgather(),MPI_Scan(),MPI_Reduce_Scatter()
▶ 八个常用的集合通信函数 ▶ 规约函数 MPI_Reduce(),将通信子内各进程的同一个变量参与规约计算,并向指定的进程输出计算结果 ● 函数原型 MPI_METHOD MPI_Reduce( _ ...
- 【MPI】并行奇偶交换排序
typedef long long __int64; #include "mpi.h" #include <cstdio> #include <algorithm ...
- MPI 集合通信函数 MPI_Scatterv(),MPI_Gatherv(),MPI_Allgatherv(),MPI_Alltoall(),MPI_Alltoallv(),MPI_Alltoallw()
▶ 函数 MPI_Scatterv() 和 MPI_Gatherv() .注意到函数 MPI_Scatter() 和 MPI_Gather() 只能向每个进程发送或接受相同个数的元素,如果希望各进程获 ...
- 【MPI学习4】MPI并行程序设计模式:非阻塞通信MPI程序设计
这一章讲了MPI非阻塞通信的原理和一些函数接口,最后再用非阻塞通信方式实现Jacobi迭代,记录学习中的一些知识. (1)阻塞通信与非阻塞通信 阻塞通信调用时,整个程序只能执行通信相关的内容,而无法执 ...
- MPI实现并行奇偶排序
奇偶排序 odd-even-sort, using MPI 代码在 https://github.com/thkkk/odd-even-sort 使用 MPI 实现奇偶排序算法, 并且 MPI 进程 ...
- 【MPI学习6】MPI并行程序设计模式:具有不连续数据发送的MPI程序设计
基于都志辉老师<MPI并行程序设计模式>第14章内容. 前面接触到的MPI发送的数据类型都是连续型的数据.非连续类型的数据,MPI也可以发送,但是需要预先处理,大概有两类方法: (1)用户 ...
- 【MPI学习2】MPI并行程序设计模式:对等模式 & 主从模式
这里的内容主要是都志辉老师<高性能计算之并行编程技术——MPI并行程序设计> 书上有一些代码是FORTAN的,我在学习的过程中,将其都转换成C的代码,便于统一记录. 这章内容分为两个部分: ...
- 利用共享内存实现比NCCL更快的集合通信
作者:曹彬 | 旷视 MegEngine 架构师 简介 从 2080Ti 这一代显卡开始,所有的民用游戏卡都取消了 P2P copy,导致训练速度显著的变慢.针对这种情况下的单机多卡训练,MegEng ...
- oracle之集合操作函数---minus、union、intersect
集合操作符专门用于合并多条select语句的结果,包括:UNION,UNION ALL,INTERSECT,MINUS.当使用集合操作函数时,需保证数据集的字段数据类型和数目一致. 使用集合操作符需要 ...
随机推荐
- Java IO流-字符流
2017-11-05 18:34:39 Java中的IO流按数据类型分类分为两种,一是字节流,二是字符流.字符流的出现是为了简化文本数据的读入和写出操作. 如果操作的文件是文本文件,那么使用字符流会大 ...
- ADSL拨号上网或者光纤上网设置概要(原创)
不管是在梧州设置光纤还是在太平设置ADSL拨号上网每次设置上网一体机的时候都是遇到各种麻烦...这次又是弄了N久,每次问题各不一样.总结一下操作过程,方便以后又遇问题回头查询自个微博.一.设置电话线的 ...
- ✅javascript 语法:附加子节点
received: function(data) { $("#notifications").prepend(data.html); } 如何用原生js写出jquery的功能: 先 ...
- bzoj1069: [SCOI2007]最大土地面积 凸包+旋转卡壳求最大四边形面积
在某块平面土地上有N个点,你可以选择其中的任意四个点,将这片土地围起来,当然,你希望这四个点围成的多边形面积最大. 题解:先求出凸包,O(n)枚举旋转卡壳,O(n)枚举另一个点,求最大四边形面积 /* ...
- IOS-static cell 与 dynamic cell 混合使用
static cell 与 dynamic cell 混合使用 关于静态cell与动态cell的混合使用,google一下便会有很多相关文章,这里也是看过一些前辈的经验(已经忘记具体是从哪篇文章得到的 ...
- MAP 最大后验——利用经验数据获得对未观测量的点态估计
Map (最大后验) 在贝叶斯统计学中,最大后验(Maximum A Posteriori,MAP)估计可以利用经验数据获得对未观测量的点态估计.它与Fisher的最大似然估计(Maximum Lik ...
- keil软件相关问题汇总
目录 一.keilc51——F12无法定位到定义处. 二.关于STM37F7的pack包不能安装报错SVD问题. 三.关于"Insufficient RAM for Flash Algori ...
- 【转】HTTP协议—— 简单认识TCP/IP协议
转自:www.cnblogs.com/roverliang/p/5176456.html 大学没读计算机专业,所以很多的专业知识都不知道.既然已经从事了IT这个行业,就势必要去了解下网络底层,虽然实际 ...
- openssl RSA证书签发命令
一.生成根证书 1. 创建根证书私钥 openssl genrsa -out ca.key 2048 2. 创建根证书申请文件 openssl req -new -key ca.key -out ca ...
- java IO 学习(二)
文件表示形式的转换: 一.从系统文件变成java中可以使用的文件对象 File file = new FIle("文件的路径"); 二.读取文件系统中文件的原始字节流,要读取字符流 ...