奇偶排序

odd-even-sort, using MPI

代码在 https://github.com/thkkk/odd-even-sort

使用 MPI 实现奇偶排序算法, 并且 MPI 进程 只能向其相邻进程发送消息

nprocs 是进程数。 每个进程拥有独立的一块数据 data[0 ~ block_len-1],组合起来为整个待排序的数组。

方法

每个阶段排序之后不进行check

此前,在每个阶段的奇偶排序进行完之后,都会进行一次进程之间的信息传递,以判断排序是否完成,这个过程要进行约\(3*nprocs\)次的send/recv。现在的优化是:总共只进行nprocs轮排序,不再进行check。这样的话,即使是目前在最小编号进程中的元素,而它值较大,本应排序到最大编号进程中,也可以在nprocs轮中排到正确的位置。

这样之后,大约有几十ms的优化。

进程之间互相传递数据,然后进行优化后的归并

在一个排序阶段中,相邻进程块互相发送自己的全部数据,之后在每个块内部将两个块的数据进行归并,但是只保留最小/最大的block_len个元素,将其拷贝到自己的data上。这样可以省掉一半的归并时间。

这样之后大约有100+ms的优化。

进程之间发送全部数据之前,先发送端点处的数据

进程之间发送全部数据之前,先发送端点处的数据,判断左边进程中的最大元素是否小于等于右边进程中的最小元素,如果是,那么无需进行后续数据的发送和归并。

这样之后大约有几十ms的优化。

代码

  1. #include <algorithm>
  2. #include <cassert>
  3. #include <cstdio>
  4. #include <cstdlib>
  5. #include <mpi.h>
  6. #include <cmath>
  7. #include "worker.h"
  8. using namespace std;
  9. bool is_edge(int rank, bool odd_or_even, bool last_rank){
  10. if (odd_or_even == 0){
  11. return (rank & 1) == 0 && last_rank;
  12. }
  13. else{
  14. return rank == 0 || ((rank & 1) == 1 && last_rank);
  15. }
  16. }
  17. void merge_left(float *A, int nA, float *B, int nB, float *C){ //make sure C[nA-1] is available
  18. float *p1 = A, *A_end = A + nA, *p2 = B, *B_end = B + nB, *p = C, *C_end = C + nA;
  19. while( p != C_end && p1 != A_end && p2 != B_end)
  20. *(p++) = ((*p1) <= (*p2)) ? *(p1++) : *(p2++);
  21. while( p != C_end )
  22. *(p++) = *(p1++);
  23. }
  24. void merge_right(float *A, int nA, float *B, int nB, float *C){
  25. float *p1 = A + nA , *p2 = B + nB , *p = C + nB;
  26. while( p != C && p1 != A && p2 != B )
  27. *(--p) = (*(p1-1) >= *(p2-1)) ? *(--p1) : *(--p2);
  28. while( p != C )
  29. *(--p) = *(--p2);
  30. }
  31. void Worker::sort() {
  32. //data[0, block_len)
  33. if (out_of_range) return ;
  34. std::sort(data, data + block_len);
  35. //先把当前进程数据排好序
  36. if (nprocs == 1) return ;
  37. bool odd_or_even = 0; // = 0: even; = 1: odd;
  38. float *cp_data = new float [block_len];
  39. float *adj_data = new float [ceiling(n, nprocs)];
  40. int limit = nprocs;
  41. while(limit--){
  42. if(is_edge(rank, odd_or_even, last_rank)){
  43. //边界情况,没有与其他进程存在于同一个进程块内
  44. }
  45. else if((rank & 1) == odd_or_even){ //receive info
  46. size_t adj_block_len = std::min(block_len, n - (rank + 1) * block_len);
  47. MPI_Request request[2];
  48. MPI_Isend(data + block_len - 1, 1, MPI_FLOAT, rank + 1, 0, MPI_COMM_WORLD, &request[0]);
  49. MPI_Irecv(adj_data, 1, MPI_FLOAT, rank + 1, 1, MPI_COMM_WORLD, &request[1]);
  50. MPI_Wait(&request[0], MPI_STATUS_IGNORE);
  51. MPI_Wait(&request[1], MPI_STATUS_IGNORE); //发送端点数据
  52. if(data [block_len - 1] > adj_data[0]) {
  53. //此时两块之间存在未排好序的数据,需要排序
  54. MPI_Sendrecv(data, block_len, MPI_FLOAT, rank + 1, 0,
  55. adj_data, adj_block_len, MPI_FLOAT, rank + 1, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
  56. //互相交换数据
  57. // merge
  58. merge_left(data, (int)block_len, adj_data, (int)adj_block_len, cp_data);
  59. //进行归并排序,取前block_len个数据返回到cp_data中
  60. memcpy(data, cp_data, block_len * sizeof(float)); //拷贝回data
  61. }
  62. }
  63. else if ((rank & 1) == !odd_or_even){ //send info
  64. size_t adj_block_len = ceiling(n, nprocs);
  65. MPI_Request request[2];
  66. MPI_Isend(data, 1, MPI_FLOAT, rank - 1, 1, MPI_COMM_WORLD, &request[1]);
  67. MPI_Irecv(adj_data + adj_block_len - 1, 1, MPI_FLOAT, rank
  68. - 1, 0, MPI_COMM_WORLD, &request[0]);
  69. MPI_Wait(&request[1], MPI_STATUS_IGNORE);
  70. MPI_Wait(&request[0], MPI_STATUS_IGNORE);
  71. //发送端点数据
  72. if (adj_data[adj_block_len - 1] > data[0]){
  73. //此时两块之间存在未排好序的数据,需要排序
  74. MPI_Sendrecv(data, block_len, MPI_FLOAT, rank - 1, 1,
  75. adj_data, adj_block_len, MPI_FLOAT, rank - 1, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
  76. //互相交换数据
  77. // merge
  78. merge_right(adj_data, (int)adj_block_len, data, (int)block_len, cp_data);
  79. //进行归并排序,取前block_len个数据返回到cp_data中
  80. memcpy(data, cp_data, block_len * sizeof(float)); //拷贝回data
  81. }
  82. }
  83. odd_or_even ^= 1;
  84. }
  85. delete[] cp_data;
  86. delete[] adj_data;
  87. }

实验数据

n N\(\times\) P 耗时(ms) 相对单进程的加速比
100000000 1$\times$1 12728.326000 1
100000000 1$\times$2 6754.229000 1.884
100000000 1$\times$4 3559.514000 3.576
100000000 1$\times$8 2007.818000 6.339
100000000 1$\times$16 1340.771000 9.493
100000000 2$\times$16 870.302000 14.625

MPI实现并行奇偶排序的更多相关文章

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

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

  2. Hark的数据结构与算法练习之奇偶排序

    算法说明 奇偶排序又叫奇偶换位排序,砖排序.它是一种交换排序,也是冒泡的一个变种 顾名思义,奇偶排序,其实就是先循环奇数位,然后将奇数位与偶数位比较计算. 然后再循环偶数位,再和奇数位比较运算.看一下 ...

  3. OpenJudge计算概论-整数奇偶排序

    /*===================================== 整数奇偶排序 总时间限制: 1000ms 内存限制: 65536kB 描述 输入10个整数,彼此以空格分隔 重新排序以后 ...

  4. 排序算法之奇偶排序 JAVA奇偶排序算法

    奇偶排序法的思路是在数组中重复两趟扫描.第一趟扫描选择所有的数据项对,a[j]和a[j+1],j是奇数(j=1, 3, 5……).如果它们的关键字的值次序颠倒,就交换它们.第二趟扫描对所有的偶数数据项 ...

  5. Openjudge-计算概论(A)-整数奇偶排序

    描述: 输入10个整数,彼此以空格分隔重新排序以后输出(也按空格分隔),要求:1.先输出其中的奇数,并按从大到小排列:2.然后输出其中的偶数,并按从小到大排列.输入任意排序的10个整数(0-100), ...

  6. LeetCode905.按奇偶排序数组

    905.按奇偶排序数组 问题描述 给定一个非负整数数组 A,返回一个由 A 的所有偶数元素组成的数组,后面跟 A 的所有奇数元素. 你可以返回满足此条件的任何数组作为答案. 示例 输入:[3,1,2, ...

  7. OpenJudge计算概论-奇偶排序

    /*==============================================总时间限制: 1000ms 内存限制: 65536kB描述 输入十个整数,将十个整数按升序排列输出,并且 ...

  8. P1021 整数奇偶排序

    整数奇偶排序 题目出处:<信息学奥赛一本通>第二章上机练习6,略有改编 题目描述 告诉你包含 \(n\) 个数的数组 \(a\) ,你需要把他们按照"奇数排前面,偶数排后面:奇数 ...

  9. 每日一题20201112(922. 按奇偶排序数组 II)

    题目链接: 922. 按奇偶排序数组 II 思路 很简单,搞懂问题的核心就行,假设现在有奇数在偶数位上,偶数在奇数位上. 那么我们要做的就是,找到分别在对方位置上的数字,然后交换他们就行. class ...

随机推荐

  1. Linux 的常用基本命令

    一.Linux 的常用基本命令..................................................................................... ...

  2. 不会提交 PR 的小伙伴看过来,超详细的视频教程!

    点击上方 蓝字关注我们 作者 | 严天奇 ✎ 编 者 按 最近有一些新加入社区的朋友反馈不太了解 Apache DolphinScheduler 提交 PR 的步骤和规则.这不,人帅心美的严天奇同学就 ...

  3. Excelize 2.3.1 发布,Go 语言 Excel 文档基础库,支持加密表格文档

    Excelize 是 Go 语言编写的用于操作 Office Excel 文档基础库,基于 ECMA-376,ISO/IEC 29500 国际标准.可以使用它来读取.写入由 Microsoft Exc ...

  4. Windows批量修改文件

    如图我是建立了壁纸文件夹 Windows自带的排序方式 如何不用自带的呢? 在这个文件夹里面建一个.txt文件 如下 ok第二步骤 将UTF-8格式改为ANSI格式 点击文件-另存为ANSI格式-替换 ...

  5. 1.3_HTML基础知识

    打开记事本,输入 <html> <hand> <title>我要自学网</title> </hand> <body> <h ...

  6. 持久化-Word库加载项劫持

    持久化-Word库加载项劫持 利用wll.xll和dll的特性来利用的 重点利用office word的信任文件来进行加载恶意代码

  7. 简单创建一个SpringCloud2021.0.3项目(一)

    目录 1. 项目说明 1. 版本 2. 用到组件 3. 功能 2. 新建父模块和注册中心 1. 新建父模块 2. 新建注册中心Eureka 3. 新建配置中心Config 4. 新建两个业务服务 1. ...

  8. Netty整合STOMP

    1.STOMP协议简介 常用的WebSocket协议定义了两种传输信息类型:文本信息和二进制信息.类型虽然被确定,但是他们的传输体是没有规定的,也就是说传输体可以自定义成什么样的数据格式都行,只要客户 ...

  9. KingbaseES R6 集群物理copy方式手工添加新备库节点

    案例说明: 对于主库数据量比较大的环境,在添加新节点是可以采用在线clone方式创建新的备库节点,也可以在离线的状态下,直接拷贝其中一个备库的所有集群相关目录来创建新的备库节点.本案例介绍了通过离线物 ...

  10. zabbix客户端无法上传数据

    昨天发现有一台Windows服务器无法向zabbix服务端汇报数据.经过检查Windows上的客户端日志,可以看到以下错误. 31976:20200206:154445.873 active chec ...