▶ 《并行程序设计导论》第三章的例子程序

● 代码

 #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()的更多相关文章

  1. MPI 集合通信函数 MPI_Reduce(),MPI_Allreduce(),MPI_Bcast(),MPI_Scatter(),MPI_Gather(),MPI_Allgather(),MPI_Scan(),MPI_Reduce_Scatter()

    ▶ 八个常用的集合通信函数 ▶ 规约函数 MPI_Reduce(),将通信子内各进程的同一个变量参与规约计算,并向指定的进程输出计算结果 ● 函数原型 MPI_METHOD MPI_Reduce( _ ...

  2. 【MPI】并行奇偶交换排序

    typedef long long __int64; #include "mpi.h" #include <cstdio> #include <algorithm ...

  3. MPI 集合通信函数 MPI_Scatterv(),MPI_Gatherv(),MPI_Allgatherv(),MPI_Alltoall(),MPI_Alltoallv(),MPI_Alltoallw()

    ▶ 函数 MPI_Scatterv() 和 MPI_Gatherv() .注意到函数 MPI_Scatter() 和 MPI_Gather() 只能向每个进程发送或接受相同个数的元素,如果希望各进程获 ...

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

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

  5. MPI实现并行奇偶排序

    奇偶排序 odd-even-sort, using MPI 代码在 https://github.com/thkkk/odd-even-sort 使用 MPI 实现奇偶排序算法, 并且 MPI 进程 ...

  6. 【MPI学习6】MPI并行程序设计模式:具有不连续数据发送的MPI程序设计

    基于都志辉老师<MPI并行程序设计模式>第14章内容. 前面接触到的MPI发送的数据类型都是连续型的数据.非连续类型的数据,MPI也可以发送,但是需要预先处理,大概有两类方法: (1)用户 ...

  7. 【MPI学习2】MPI并行程序设计模式:对等模式 & 主从模式

    这里的内容主要是都志辉老师<高性能计算之并行编程技术——MPI并行程序设计> 书上有一些代码是FORTAN的,我在学习的过程中,将其都转换成C的代码,便于统一记录. 这章内容分为两个部分: ...

  8. 利用共享内存实现比NCCL更快的集合通信

    作者:曹彬 | 旷视 MegEngine 架构师 简介 从 2080Ti 这一代显卡开始,所有的民用游戏卡都取消了 P2P copy,导致训练速度显著的变慢.针对这种情况下的单机多卡训练,MegEng ...

  9. oracle之集合操作函数---minus、union、intersect

    集合操作符专门用于合并多条select语句的结果,包括:UNION,UNION ALL,INTERSECT,MINUS.当使用集合操作函数时,需保证数据集的字段数据类型和数目一致. 使用集合操作符需要 ...

随机推荐

  1. 这些HTML、CSS知识点,面试和平时开发都需要 No8-No9(知识点:媒体操作、构建表单)

    系列知识点汇总 这些HTML.CSS知识点,面试和平时开发都需要 No1-No4(知识点:HTML.CSS.盒子模型.内容布局) 这些HTML.CSS知识点,面试和平时开发都需要 No5-No7(知识 ...

  2. MVP框架模式

    一.基本概念 MVP是Model-View-Presenter的简称,即模型-视图-表现层的缩写.MVP是由MVC模式进化而来的,MVP改进了MVC中的控制器过于臃肿的问题.与MVC一样,MVP将应用 ...

  3. Rails 5 Test Prescriptions(everday Rspectest作者推荐) 目录 1-3章

    总文档连接: RSpec.info/documentation/ 如何使用TDD 和 自动化测试来建立一个Rails app. TDD让你用测试来探索代码的设计.你将学习可利用的工具,并学习用什么工具 ...

  4. 玲珑杯 round18 A 计算几何瞎暴力

    题目链接 : http://www.ifrog.cc/acm/problem/1143 当时没看到坐标的数据范围= =看到讨论才意识到,不同的坐标最多只有1k多个,完全可以暴力做法,不过也要一些技巧. ...

  5. UVA-11613 Acme Corporation (最大费用最大流+拆点)

    题目大意:有一种商品X,其每每单位存放一个月的代价I固定.并且已知其每月的最大生产量.生产每单位的的代价.最大销售量和销售单价,还已知每个月生产的X能最多能存放的时间(以月为单位).问只考虑前m个月, ...

  6. ES6学习一 JS语言增强篇

    一 背景 JavaScript经过二十来年年的发展,由最初简单的交互脚本语言,发展到今天的富客户端交互,后端服务器处理,跨平台(Native),以及小程序等等的应用.JS的角色越来越重要,处理场景越来 ...

  7. 关于protel 99se 汉化后某些菜单消失的解决方法

    本人在使用protel 99se 画PCB时,遇到了好些问题,通过网上查资料基本都解决了. 下面给大家分享 关于protel 99se 汉化后某些菜单消失的解决方法. 其他的许多看不见的菜单也可以自己 ...

  8. 2017年7月ROS学习资料小结

    <孙子兵法·谋攻篇>:"上兵伐谋,其次伐交,其次伐兵,其下攻城:攻城之法为不得已." 任何发生在自己国土上的战争,即便胜利,也饱含屈辱. ----~~~~----Gaz ...

  9. ionic2中跨页面回传值

    1.在跳转到新页面时传入一个contactsCallback的参数,在该参数的函数定义中做出一个承诺. 注意:最开始我本来是采用如下图方式的,但是很不幸,出现了问题,问题所在就是关于这个this的作用 ...

  10. 利用U盘大白菜软件来重装win7系统

    个人装win7系统用了两个U盘,一个做启动盘(FAT32格式),另外一个当做系统盘(NTFS格式). 首先在电脑里面下载一个大白菜软件,并且安装好,打开软件,插上U盘,检测到了该U盘即可一键制作启动盘 ...