MPI 集合通信函数 MPI_Reduce(),MPI_Allreduce(),MPI_Bcast(),MPI_Scatter(),MPI_Gather(),MPI_Allgather(),MPI_Scan(),MPI_Reduce_Scatter()
▶ 八个常用的集合通信函数
▶ 规约函数 MPI_Reduce(),将通信子内各进程的同一个变量参与规约计算,并向指定的进程输出计算结果
● 函数原型
MPI_METHOD MPI_Reduce(
_In_range_(!= , recvbuf) _In_opt_ const void* sendbuf, // 指向输入数据的指针
_When_(root != MPI_PROC_NULL, _Out_opt_) void* recvbuf, // 指向输出数据的指针,即计算结果存放的地方
_In_range_(>= , ) int count, // 数据尺寸,可以进行多个标量或多个向量的规约
_In_ MPI_Datatype datatype, // 数据类型
_In_ MPI_Op op, // 规约操作类型
_mpi_coll_rank_(root) int root, // 目标进程号,存放计算结果的进程
_In_ MPI_Comm comm // 通信子
);
● 使用范例
{
int size, rank, data, dataCollect;
MPI_Init(NULL, NULL);
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank); data = rank;// 参与计算的数据
MPI_Reduce((void *)&data, (void *)&dataCollect, , MPI_INT, MPI_SUM, , MPI_COMM_WORLD);// 所有的进程都要调用,而不是只在目标进程中调用 MPI_Finalize();
}
● 操作类型,定义于 mpi.h
#define MPI_OP_NULL ((MPI_Op)0x18000000) #define MPI_MAX ((MPI_Op)0x58000001)
#define MPI_MIN ((MPI_Op)0x58000002)
#define MPI_SUM ((MPI_Op)0x58000003)
#define MPI_PROD ((MPI_Op)0x58000004)
#define MPI_LAND ((MPI_Op)0x58000005)// 逻辑与
#define MPI_BAND ((MPI_Op)0x58000006)// 按位与
#define MPI_LOR ((MPI_Op)0x58000007)
#define MPI_BOR ((MPI_Op)0x58000008)
#define MPI_LXOR ((MPI_Op)0x58000009)
#define MPI_BXOR ((MPI_Op)0x5800000a)
#define MPI_MINLOC ((MPI_Op)0x5800000b)// 求最小值所在位置
#define MPI_MAXLOC ((MPI_Op)0x5800000c)// 求最大值所在位置
#define MPI_REPLACE ((MPI_Op)0x5800000d)
▶ 规约并广播函数 MPI_Allreduce(),在计算规约的基础上,将计算结果分发到每一个进程中,相比于 MPI_Reduce(),只是少了一个 root 参数。除了简单的先规约再广播的方法,书中介绍了蝶形结构全局求和的方法。
● 函数原型
_Pre_satisfies_(recvbuf != MPI_IN_PLACE) MPI_METHOD MPI_Allreduce(
_In_range_(!= , recvbuf) _In_opt_ const void* sendbuf,
_Out_opt_ void* recvbuf,
_In_range_(>= , ) int count,
_In_ MPI_Datatype datatype,
_In_ MPI_Op op,
_In_ MPI_Comm comm
);
● 使用范例
{
int size, rank, data, dataCollect;
MPI_Init(NULL, NULL);
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank); data = rank;
MPI_Reduce((void *)&data, (void *)&dataCollect, , MPI_INT, MPI_SUM, MPI_COMM_WORLD);// 所有的进程都要调用 MPI_Finalize();
}
▶ 广播函数 MPI_Bcast(),将某个进程的某个变量的值广播到该通信子中所有进程的同名变量中
● 函数原型
MPI_METHOD MPI_Bcast(
_Pre_opt_valid_ void* buffer, // 指向输入 / 输出数据的指针
_In_range_(>= , ) int count, // 数据尺寸
_In_ MPI_Datatype datatype, // 数据类型
_mpi_coll_rank_(root) int root, // 广播源进程号
_In_ MPI_Comm comm // 通信子
);
● 使用范例
{
int size, rank, data;
MPI_Init(NULL, NULL);
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank); data = rank;
MPI_Bcast((void *)&data, , MPI_INT, , MPI_COMM_WORLD);// 所有的进程都要调用,调用后所有 data 均被广播源进程的值覆盖 MPI_Finalize();
}
▶ 散射函数 MPI_Scatter(),将向量数据分段发送到各进程中
● 函数原型和宏定义
_Pre_satisfies_(sendbuf != MPI_IN_PLACE) MPI_METHOD MPI_Scatter(
_In_range_(!= , recvbuf) _In_opt_ const void* sendbuf, // 指向需要分发的数据的指针
_In_range_(>= , ) int sendcount, // 分发到每一个进程的数据量,注意不是分发的数据总量
_In_ MPI_Datatype sendtype, // 分发数据类型
_When_(root != MPI_PROC_NULL, _Out_opt_) void* recvbuf, // 指向接收的数据的指针
_In_range_(>= , ) int recvcount, // 接受数据量,不小于上面分发到每一个进程的数据量
_In_ MPI_Datatype recvtype, // 接收数据类型
_mpi_coll_rank_(root) int root, // 分发数据源进程号
_In_ MPI_Comm comm // 通信子
); // 宏定义,mpi.h
#define MPI_IN_PLACE ((void*)(MPI_Aint)-1 // MPI_Aint 为 __int64 类型,表示地址
▶ 聚集函数 MPI_Gather(),将各进程中的向量数据分段聚集到一个进程的大向量中
● 函数原型
_Pre_satisfies_(recvbuf != MPI_IN_PLACE) MPI_METHOD MPI_Gather(
_In_opt_ _When_(sendtype == recvtype, _In_range_(!= , recvbuf)) const void* sendbuf,// 指向需要聚集的数据的指针
_In_range_(>= , ) int sendcount, // 每个进程中进行聚集的数据量,不是聚集的数据总量
_In_ MPI_Datatype sendtype, // 发送数据类型
_When_(root != MPI_PROC_NULL, _Out_opt_) void* recvbuf, // 指向接收数据的指针
_In_range_(>= , ) int recvcount, // 从每个进程接收的接收数据量,不是聚集的数据总量
_In_ MPI_Datatype recvtype, // 接收数据类型
_mpi_coll_rank_(root) int root, // 聚集数据汇进程号
_In_ MPI_Comm comm // 通信子
);
● 函数 MPI_Scatter() 和 MPI_Gather() 的范例
{
const int dataSize = * ;
const int localSize = ;
int globalData[dataSize], localData[localSize], globalSum, i, comSize, comRank; MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &comSize);
MPI_Comm_rank(MPI_COMM_WORLD, &comRank); if (comRank == ) // 初始化
for (i = ; i < dataSize; globalData[i] = i, i++);
for (i = ; i < localSize; localData[i++] = ); MPI_Scatter((void *)&globalData, localSize, MPI_INT, (void *)&localData, localSize, MPI_INT, , MPI_COMM_WORLD); // 分发数据
for (i = ; i < localSize; localData[i++]++);
MPI_Barrier(MPI_COMM_WORLD); // 进程同步
MPI_Gather((void *)&localData, localSize, MPI_INT, (void *)&globalData, localSize, MPI_INT, , MPI_COMM_WORLD); // 聚集数据
for (i = globalSum = ; i < dataSize; globalSum += globalData[i++]); if (comRank == )
printf("\nSize = %d, Rank = %d, result = %d\n", comSize, comRank, globalSum);
MPI_Finalize(); return ;// 输出结果:Size = 8, Rank = 0, result = 2080,表示 0 + 1 + 2 + …… + 63
}
▶ 全局聚集函数 MPI_Allgather(),将各进程的向量数据聚集为一个大向量,并分发到每个进程中,相当于各进程同步该大向量的各部分分量。相比于 MPI_Gather(),只是少了一个 root 参数。
● 函数原型
_Pre_satisfies_(recvbuf != MPI_IN_PLACE) MPI_METHOD MPI_Allgather(
_In_opt_ _When_(sendtype == recvtype, _In_range_(!= , recvbuf)) const void* sendbuf,
_In_range_(>= , ) int sendcount,
_In_ MPI_Datatype sendtype,
_Out_opt_ void* recvbuf,
_In_range_(>= , ) int recvcount,
_In_ MPI_Datatype recvtype,
_In_ MPI_Comm comm
);
● 函数 MPI_Scatter() 和 MPI_Allgather() 的范例,相当于从上面的范例中修改了一部分
{
const int dataSize = * ;
const int localSize = ;
int globalData[dataSize], localData[localSize], globalSum, i, comSize, comRank; MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &comSize);
MPI_Comm_rank(MPI_COMM_WORLD, &comRank); for (i = ; i < dataSize; globalData[i] = i, i++);// 改动
for (i = ; i < localSize; localData[i++] = ); MPI_Scatter((void *)&globalData, localSize, MPI_INT, (void *)&localData, localSize, MPI_INT, , MPI_COMM_WORLD); // 分发数据
for (i = ; i < localSize; localData[i++]++);
MPI_Barrier(MPI_COMM_WORLD);
MPI_Allgather((void *)&localData, localSize, MPI_INT, (void *)&globalData, localSize, MPI_INT, MPI_COMM_WORLD); // 聚集数据,改动
for (i = globalSum = ; i < dataSize; globalSum += globalData[i++]); printf("\nSize = %d, rank = %d, result = %d\n", comSize, comRank, globalSum);// 改动
MPI_Finalize(); return ;// 输出结果,八个进程乱序输出 2080
}
▶ 前缀和函数 MPI_Scan(),将通信子内各进程的同一个变量参与前缀规约计算,并将得到的结果发送回每个进程,使用与函数 MPI_Reduce() 相同的操作类型
● 函数原型
_Pre_satisfies_(recvbuf != MPI_IN_PLACE) MPI_METHOD MPI_Scan(
_In_opt_ _In_range_(!= , recvbuf) const void* sendbuf, // 指向参与规约数据的指针
_Out_opt_ void* recvbuf, // 指向接收规约结果的指针
_In_range_(>= , ) int count, // 每个进程中参与规约的数据量
_In_ MPI_Datatype datatype, // 数据类型
_In_ MPI_Op op, // 规约操作类型
_In_ MPI_Comm comm // 通信子
);
● 范例代码
int main(int argc, char **argv)
{
const int nProcess = , localSize = , globalSize = localSize * nProcess;
int globalData[globalSize], localData[localSize], sumData[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_Scan(localData, sumData, localSize, MPI_INT, MPI_SUM, MPI_COMM_WORLD); for (i = ; i < localSize; i++)
printf("%2d, ", sumData[i]); MPI_Finalize();
return ;
}
● 输出结果,分别展示了 localSize 取 1 和 8 的结果,每个进程的输出中,前一半(分别为 1 个和 8 个元素)为个进程的原始数据,后一半为进行完前缀求和后的结果。注意到 localSize 取 8 时,程序将各进程保存向量的每一个元素分别进行前缀和,但同一进程中各元素之间不相互影响。
D:\Code\MPI\MPIProjectTemp\x64\Debug>mpiexec -n -l MPIProjectTemp.exe
[] , ,
[] , ,
[] , ,
[] , ,
[] , ,
[] , ,
[] , ,
[] , ,
D:\Code\MPI\MPIProjectTemp\x64\Debug>mpiexec -n -l MPIProjectTemp.exe
[] , , , , , , , , , , , , , , , ,
[] , , , , , , , , , , , , , , , ,
[] , , , , , , , , , , , , , , , ,
[] , , , , , , , , , , , , , , , ,
[] , , , , , , , , , , , , , , , ,
[] , , , , , , , , , , , , , , , ,
[] , , , , , , , , , , , , , , , ,
[] , , , , , , , , , , , , , , , ,
▶ 规约分发函数 MPI_Reduce_Scatter(),将数据进行规约计算,结果分段分发到各进程中
● 函数原型
_Pre_satisfies_(recvbuf != MPI_IN_PLACE) MPI_METHOD MPI_Reduce_scatter(
_In_opt_ _In_range_(!= , recvbuf) const void* sendbuf, // 指向输入数据的指针
_Out_opt_ void* recvbuf, // 指向接收数据的指针
_In_ const int recvcounts[], // 各进程接收规约结果的元素个数
_In_ MPI_Datatype datatype, // 数据类型
_In_ MPI_Op op, // 规约操作类型
_In_ MPI_Comm comm // 通信子
);
● 使用范例
{
const int nProcess = , localSize = , globalSize = nProcess * localSize, countValue = ;
int globalData[globalSize], localData[localSize], count[localSize], localSum[countValue], i, comSize, comRank; MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &comSize);
MPI_Comm_rank(MPI_COMM_WORLD, &comRank); 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; count[i++] = ); for (i = ; i < localSize; i++)
printf("%3d, ", localData[i]); MPI_Reduce_scatter(localData, localSum, count, MPI_INT, MPI_SUM, MPI_COMM_WORLD); for (i = ; i < countValue; i++)
printf("%3d, ", localSum[i]); MPI_Finalize();
return ;
}
● 输出结果,这里取定 localSize 为 8,输出结果的前 8 个元素为分发到各进程中参与规约计算的原始数据,后面元素为规约计算结果。程序将各进程保存向量的每一个元素分别进行前缀和,但同一进程中各元素之间不相互影响,通过修改 countValue(即参数 count 各元素的值),可以将规约计算的结果分发到各进程中
■ countValue == 1(count == { 1, 1, 1, 1, 1, 1, 1, 1 })情况,每个进程分得一个结果(注意与上面的函数 MPI_Scan() 作对比)
■ countValue == 2(count == { 2, 2, 2, 2, 2, 2, 2, 2 })情况,前 4 个进程每个进程分得 2 个结果,后 4 的进程访问越界,得到无意义的值
■ count == { 2, 0, 2, 0, 2, 0, 2, 0 } 情况,偶数号进程每个进程分得 2 个结果,奇数号进程分得 0 个结果,表现为无意义的值
■ 思考,这列每个 localData 长度为 8,所以规约计算的结果为一个长度为 8 的向量,可以在不同进程中进行分发(注意数据尺寸大小 localSize 与运行程序的进程数 nProcess 没有任何关系,只是在范例中恰好相等),而函数 MPI_Scan() 则相当于在此基础上保留了所有中间结果(部分前缀结果),所以其输出为一个长为 localSize,宽度为 nProcess 的矩阵,并且自动按照进程号均分。
D:\Code\MPI\MPIProjectTemp\x64\Debug>mpiexec -n -l MPIProjectTemp.exe // countValue = 1
[] , , , , , , , , ,
[] , , , , , , , , ,
[] , , , , , , , , ,
[] , , , , , , , , ,
[] , , , , , , , , ,
[] , , , , , , , , ,
[] , , , , , , , , ,
[] , , , , , , , , , D:\Code\MPI\MPIProjectTemp\x64\Debug>mpiexec -n -l MPIProjectTemp.exe // countValue = 2
[] , , , , , , , , , ,
[] , , , , , , , , , ,
[] , , , , , , , , , ,
[] , , , , , , , , , ,
[] , , , , , , , , , ,
[] , , , , , , , , , ,
[] , , , , , , , , , ,
[] , , , , , , , , , , D:\Code\MPI\MPIProjectTemp\x64\Debug>mpiexec -n -l MPIProjectTemp.exe // countValue = 2,count[i] = (i + 1) % 2 * 2
[] , , , , , , , , , ,
[] , , , , , , , , , ,
[] , , , , , , , , -, -,
[] , , , , , , , , -, -,
[] , , , , , , , , -, -,
[] , , , , , , , , , ,
[] , , , , , , , , -, -,
[] , , , , , , , , , ,
MPI 集合通信函数 MPI_Reduce(),MPI_Allreduce(),MPI_Bcast(),MPI_Scatter(),MPI_Gather(),MPI_Allgather(),MPI_Scan(),MPI_Reduce_Scatter()的更多相关文章
- MPI 集合通信函数 MPI_Scatterv(),MPI_Gatherv(),MPI_Allgatherv(),MPI_Alltoall(),MPI_Alltoallv(),MPI_Alltoallw()
▶ 函数 MPI_Scatterv() 和 MPI_Gatherv() .注意到函数 MPI_Scatter() 和 MPI_Gather() 只能向每个进程发送或接受相同个数的元素,如果希望各进程获 ...
- MPI 并行奇偶交换排序 + 集合通信函数 Sendrecv() Sendvecv_replace()
▶ <并行程序设计导论>第三章的例子程序 ● 代码 #include <stdio.h> #include <mpi.h> #include <stdlib. ...
- oracle之集合操作函数---minus、union、intersect
集合操作符专门用于合并多条select语句的结果,包括:UNION,UNION ALL,INTERSECT,MINUS.当使用集合操作函数时,需保证数据集的字段数据类型和数目一致. 使用集合操作符需要 ...
- (Python)集合、集合的函数
本节我们将学习python的另一种数据类型:集合(set) 1.集合(set) 集合在Python中是一种没有重复元素,且无序的数据类型,且不能通过索引来引用集合中的元素 >>> b ...
- 文成小盆友python-num3 集合,函数,-- 部分内置函数
本接主要内容: set -- 集合数据类型 函数 自定义函数 部分内置函数 一.set 集合数据类型 set集合,是一个无序且不重复的元素集合 集合基本特性 无序 不重复 创建集合 #!/bin/en ...
- Oracle集合操作函数:union、intersect、minus
[转]Oracle集合操作函数:union.intersect.minus 集合操作符专门用于合并多条select 语句的结果,包括:UNION, UNION ALL, INTERSECT, MINU ...
- redis 有序集合(zset)函数
redis 有序集合(zset)函数 zAdd 命令/方法/函数 Adds the specified member with a given score to the sorted set stor ...
- mongo的runCommand与集合操作函数的关系
除了特殊注释外,本文的测试结果均基于 spring-data-mongodb:1.10.6.RELEASE(spring-boot-starter:1.5.6.RELEASE),MongoDB 3.0 ...
- linux i2c 的通信函数i2c_transfer在什么情况下出现错误
问题: linux i2c 的通信函数i2c_transfer在什么情况下出现错误描述: linux i2c设备驱动 本人在写i2c设备驱动的时候使用i2c transfer函数进行通信的时候无法进行 ...
随机推荐
- 移动端单位rem计算
!(function(doc, win) { var docEl = doc.documentElement, resizeEvt = 'orientationchange' in window ? ...
- 冰水挑战 HDU - 6495
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6495 题解:DP!!! dp[i][j] 表示前i个挑战,接受了j个剩余的最大体力,最后输出体力大于0 ...
- Far manager界面混乱问题解决
刚装完,win7和XP上运行,都是界面混乱,看到网上说是属性里改字体,改编码,我试了半天不行: 最后发现,需要far.exe创建快捷方式,在快捷方式的属性里设置字体,编码,窗口大小,废话不多说,上图: ...
- JQ方法实用案例///鼠标移动到div和修改ipt中弹窗、CSS鼠标变小手、JQ获取元素属性、JQ选择器
今天学习了jQ,jQ对js的帮助很大,菜鸟教程上也有属性.可以查看 js 和 jquery主要的区别 在 dom 想用jquery 必须先引入(顺序问题) 先css 再js: ...
- python入门——热量转换 I
热量转换 I 描述 卡路里,简称卡(Calorie),缩写为cal,定义为在1个大气压下,将1克水提升1摄氏度所需要的热量,卡是能量单位,被广泛使用在营养计量方面.国际能量标准单位是焦耳(joule) ...
- day059-60 ajax初识 登录认证练习 form装饰器, form和ajax上传文件 contentType
一.ajax 的特点 1.异步交互:客户端发出一个请求后,需要等待服务器响应结束后, 才能发出第二个请求 2.局部刷新:给用户的感受是在不知不觉中完成请求和响应过程. 二.ajax 模板示例 ($.a ...
- (C#)生成指定长度的随机字符串的通用方法
.NET(C#)生成指定长度的随机字符串的通用方法,此方法可以指定字符串的长度,是否包含数字,是否包含符号,是否包含小写字母,是否包含大写字母等, 源码: #region 生成指定长度的随机字符串 / ...
- Open Source 开发工具集
Open Source 开发工具集 转自:http://www.linuxforum.net原作者:gogoliu(Pooh-Bah) 编辑器: vi:老牌编辑器,在各个unix和unix-like平 ...
- 【Oracle】ORA-14400: 插入的分区关键字未映射到任何分区
问题描述: 工作中使用kettle将原始库中的数据抽取到标准库中,在抽取过程中报错:[ORA-14400: 插入的分区关键字未映射到任何分区]/[ORA-14400: inserted partiti ...
- 申请Let's Encrypt永久免费SSL证书
Let's Encrypt简介 Let's Encrypt作为一个公共且免费SSL的项目逐渐被广大用户传播和使用,是由Mozilla.Cisco.Akamai.IdenTrust.EFF等组织人员发起 ...