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

● 代码

 #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. apache配置文件详解与优化

    apache配置文件详解与优化 一.总结 一句话总结:结合apache配置文件中的英文说明和配置详解一起看 1.apache模块配置用的什么标签? IfModule 例如: <IfModule ...

  2. linux系统之间共享文件(CentOS6)

    Server IP: 192.168.2.128 nfs, rpcbind(portmap) installed Client IP: 192.168.2.254 nfs, rpcbind(portm ...

  3. Hosts 文件的作用

    问题来源: 我修改了hosts文件访问公司的内网  但是出现错误找不到服务器或DNS错误  一个下午了都上不了公司的系统. Hosts是什么?Hosts是Window系统目录里的一个文件,它的作用可大 ...

  4. Anatoly and Cockroaches

    Anatoly lives in the university dorm as many other students do. As you know, cockroaches are also li ...

  5. POJ2891 Strange Way to Express Integers

    题意 Language:Default Strange Way to Express Integers Time Limit: 1000MS Memory Limit: 131072K Total S ...

  6. c/c++指针详解(一)

    一:相关概念 1.指针数组:int *p[6]               是数组,是一个存放指针的数组,也就是里面存放的是地址. 2.数组指针:int (*p)[6]                 ...

  7. hibernate enum映射详解

    hibernate enum映射详解 在这里介绍注解的形式,如果想要了解XML配置的方式,可以自行查找相关资料. 例如以下Entity @Entity @Table(name = "t_us ...

  8. PostgreSQL性能极限

    目前已有很多PostgreSQL的系统在实际生产环境下管理着超过4TB的数据.一些PostgreSQL系统的极限值如下表所列: 极限值: 最大单个数据库大小 不限 最大数据单表大小 32 TB 单条记 ...

  9. 写写Django中DRF框架概述以及序列化器对象serializer的构造方法以及使用

    写写Django中DRF框架概述以及序列化器对象serializer的构造方法以及使用 一.了解什么是DRF DRF: Django REST framework Django REST framew ...

  10. java局部变量和临时变量

    局部变量:temp=1, 临时变量:return a+b 临时变量会有一点的性能优势 局部变量会比成员变量和静态成员变量有优势,改进的方法是吧成员变量和静态成员变量赋值在局部变量:https://bl ...