并行程序VS串行程序——优化实录

开发 开发工具
透过MPI写成一个并行排序程序,但其实这是一个伪并行程序,其实质还是一个串行程序。那么究竟该怎么对比他们呢?

  在多核处理器、超级计算机日益普及的今天,程序员们怎能对并行程序“袖手旁观”呢?

  为了练手,我用MPI写了一个并行排序程序,

  先介绍下我的第一个版本,大概的思路是:

  使用MPI在各个进程之间进行通信,

  1. 进程0生成随机数,并且讲数据分段,将各段数据分配给其他进程

  2. 其他进程收到数据段,使用冒泡排序进行,发送回进程0

  3. 进程0收到这些数据,通过归并排序按顺序整合起来。

  下面是这个版本代码,

  1.   //MPI Hello World demo  
  2.   #include <mpi.h>  
  3.   #include <stdio.h>  
  4.   #include <stdlib.h>  
  5.   #include <time.h>  
  6.   #defineN 30  
  7.   intmain(intargc, char** argv)  
  8.   {  
  9.  intprocessRank, processNum, t, data, num;  
  10.   intdataArr[N];  
  11.   intdataArrB[N];  
  12.   intpointer[100];  
  13.   intsecEnd[100];  
  14.   MPI_Status mpistat;  
  15.   MPI_Init(&argc, &argv);  
  16.   MPI_Comm_size(MPI_COMM_WORLD, &processNum);  
  17.   MPI_Comm_rank(MPI_COMM_WORLD, &processRank);  
  18.   printf("Yes, Sir! From process %i of %i ", processRank, processNum);  
  19.   if(processRank == 0)  
  20.   {  
  21.   srand(time(NULL));  
  22.   for(inti = 0;i <N; i++){  
  23.   dataArr[i] = rand()%1000;  
  24.  }  
  25.   printf("Original Array: ");  
  26.   for(inti = 0;i< N; i++){  
  27.   printf("%d ", dataArr[i]);  
  28.   }  
  29.   printf(" ");  
  30.   puts("Distribute data to processes");  
  31.   for(inti = 1;i <processNum; i++){  
  32.   num = (N/(processNum-1));  
  33.  if(i == processNum -1)  
  34.   num = N - num * (processNum -2);  
  35.   ///distribute data to each process  
  36.   printf("Sending to process %d... ", i);  
  37.   MPI_Send(&num, 1, MPI_INT, i, 55, MPI_COMM_WORLD);  
  38.   MPI_Send(&dataArr[(N/(processNum-1)) * (i-1)], num, MPI_INT, i, 55, MPI_COMM_WORLD);  
  39.   ///gather the sorted data  
  40.   printf("Receiving from process %d... ", i);  
  41.   MPI_Recv(&dataArrB[(N/(processNum-1)) * (i-1)], num, MPI_INT, i, 55, MPI_COMM_WORLD, &mpistat);  
  42.   ///prepare for merge, set the pointers  
  43.   pointer[i] = (N/(processNum-1)) * (i-1);  
  44.   secEnd[i] = pointer[i] + N/(processNum-1);  
  45.   if(i == processNum-1) secEnd[i] = N;  
  46.   }  
  47.   printf("Sorted Sections Array: ");  
  48.   for(inti = 0;i< N; i++){  
  49.   printf("%d ", dataArrB[i]);  
  50.   }  
  51.   puts("");  
  52.   ///merge the sorted sections  
  53.   puts("Merging...");  
  54.   for(inti = 0;i <N; i++){  
  55.   inttMin = 1;  
  56.   intmin = 10000;  
  57.   for(t = 1;t <processNum; t++){  
  58.   if(pointer[t] <secEnd[t] &&dataArrB[pointer[t]] <min){  
  59.   min = dataArrB[pointer[t]];  
  60.   tMin = t;  
  61.   }  
  62.   }  
  63.   dataArr[i] = dataArrB[pointer[tMin]];  
  64.   pointer[tMin]++;  
  65.   }  
  66.   ///output the results  
  67.   printf("Final Sorted Array: ");  
  68.   for(inti = 0;i< N; i++){  
  69.   printf("%d ", dataArr[i]);  
  70.   }  
  71.   printf(" ");  
  72.   }  
  73.   else 
  74.   {  
  75.   //receieve the section  
  76.   MPI_Recv(&num, 1, MPI_INT, 0, 55, MPI_COMM_WORLD, &mpistat);  
  77.   MPI_Recv(&dataArr[0], num, MPI_INT, 0, 55, MPI_COMM_WORLD, &mpistat);  
  78.   printf("Received Original Array: ");  
  79.   for(inti = 0;i< num; i++){  
  80.   printf("%d ", dataArr[i]);  
  81.   }  
  82.   printf(" ");  
  83.   //sort this section  
  84.   for(inti = 0;i <num -1;i++)  
  85.   for(intj = num-1;j>=i+1;j--)  
  86.   if(dataArr[j] <dataArr[j-1]){  
  87.   inttmp = dataArr[j];  
  88.   dataArr[j]= dataArr[j-1];  
  89.   dataArr[j-1] = tmp;  
  90.   }  
  91.   MPI_Send(&dataArr[0], num, MPI_INT, 0, 55, MPI_COMM_WORLD);  
  92.   ///display  
  93.   printf("My Sorted Section: ");  
  94.   for(inti = 0;i< num; i++){  
  95.   printf("%d ", dataArr[i]);  
  96.   }  
  97.  printf(" ");  
  98.   }  
  99.   MPI_Finalize();  
  100.   return0;  
  101.   } 

  自己写出之后当然高兴,不过程序经过高手检查之后,提出了一些问题。

  最要命的是这个

  1.   for(inti = 1;i <processNum; i++){  
  2.   num = (N/(processNum-1));  
  3.   if(i == processNum -1)  
  4.   num = N - num * (processNum -2);  
  5.   ///distribute data to each process  
  6.   printf("Sending to process %d... ", i);  
  7.   MPI_Send(&num, 1, MPI_INT, i, 55, MPI_COMM_WORLD);  
  8.   MPI_Send(&dataArr[(N/(processNum-1)) * (i-1)], num, MPI_INT, i, 55, MPI_COMM_WORLD);  
  9.   ///gather the sorted data  
  10.   printf("Receiving from process %d... ", i);  
  11.   MPI_Recv(&dataArrB[(N/(processNum-1)) * (i-1)], num, MPI_INT, i, 55, MPI_COMM_WORLD, &mpistat);  
  12.   ///prepare for merge, set the pointers  
  13.   pointer[i] = (N/(processNum-1)) * (i-1);  
  14.   secEnd[i] = pointer[i] + N/(processNum-1);  
  15.   if(i == processNum-1) secEnd[i] = N;  
  16.   } 

  这段程序彻底抹杀掉了我这个并行程序的光辉形象,因为这段煞有介事的并行程序,其实是一段串行程序。

  屏幕前的高手应该看出来了吧,同一段程序的收发,都在同一段循环中。

  也就意味着,不同段之间的收发是一个接着一个的。也就意味着,其他每个进程各自的排序也是一个接着一个进行的,并不会如我初衷并行排序。

  想来,这段错误应该是并行程序小白们常犯的错误,所以我也很乐于把我做过的蠢事发出来给大家分享。前车之鉴,警钟长鸣lol

  改正之后的这段程序是这样的,

  1.   for(inti = 1;i <processNum; i++){  
  2.   num = (N/(processNum-1));  
  3.   if(i == processNum -1)  
  4.   num = N - num * (processNum -2);  
  5.   ///distribute data to each process  
  6.   printf("Sending to process %d... ", i);  
  7.   MPI_Send(&num, 1, MPI_INT, i, 55, MPI_COMM_WORLD);  
  8.   MPI_Send(&dataArr[(N/(processNum-1)) * (i-1)], num, MPI_INT, i, 55, MPI_COMM_WORLD);  
  9.   }  
  10.   for(inti = 1;i <processNum; i++){  
  11.   num = (N/(processNum-1));  
  12.   if(i == processNum -1)  
  13.   num = N - num * (processNum -2);  
  14.   ///gather the sorted data  
  15.   printf("Receiving from process %d... ", i);  
  16.   MPI_Recv(&dataArrB[(N/(processNum-1)) * (i-1)], num, MPI_INT, i, 55, MPI_COMM_WORLD, &mpistat);  
  17.   ///prepare for merge, set the pointers  
  18.   pointer[i] = (N/(processNum-1)) * (i-1);  
  19.   secEnd[i] = pointer[i] + N/(processNum-1);  
  20.   if(i == processNum-1) secEnd[i] = N;  
  21.   } 

  同时程序的效率还可以提升,比如说把其他进程排序的算法换成快排什么的。

  最后奉上优化后的版本

  1.   //MPI Hello World demo  
  2.   #include <mpi.h>  
  3.   #include <stdio.h>  
  4.   #include <stdlib.h> //'qsort' is in it.  
  5.   #include <time.h>  
  6.   #include <map>  
  7.   #defineN 30  
  8.   intQuickSortCompareFun(constvoid*p1, constvoid*p2)  
  9.   {  
  10.   return*((constint*)p1) - *((constint*)p2);  
  11.   }  
  12.   intmain(intargc, char** argv)  
  13.   {  
  14.   intprocessRank, processNum, t, data, num;  
  15.   intdataArr[N];  
  16.   intdataArrB[N];  
  17.   intpointer[100];  
  18.   intsecEnd[100];  
  19.   MPI_Status mpistat;  
  20.   MPI_Init(&argc, &argv);  
  21.   MPI_Comm_size(MPI_COMM_WORLD, &processNum);  
  22.   MPI_Comm_rank(MPI_COMM_WORLD, &processRank);  
  23.   printf("Yes, Sir! From process %i of %i ", processRank, processNum);  
  24.   if(processRank == 0)  
  25.   {  
  26.   srand(time(NULL));  
  27.   for(inti = 0;i <N; i++){  
  28.   dataArr[i] = rand()%1000;  
  29.   }  
  30.   printf("Original Array: ");  
  31.  for(inti = 0;i< N; i++){  
  32.   printf("%d ", dataArr[i]);  
  33.   }  
  34.   printf(" ");  
  35.   puts("Distribute data to processes");  
  36.   for(inti = 1;i <processNum; i++){  
  37.   num = (N/(processNum-1));  
  38.   if(i == processNum -1)  
  39.   num = N - num * (processNum -2);  
  40.   ///distribute data to each process  
  41.   printf("Sending to process %d... ", i);  
  42.   MPI_Send(&num, 1, MPI_INT, i, 55, MPI_COMM_WORLD);  
  43.   MPI_Send(&dataArr[(N/(processNum-1)) * (i-1)], num, MPI_INT, i, 55, MPI_COMM_WORLD);  
  44.   }  
  45.   for(inti = 1;i <processNum; i++){  
  46.   num = (N/(processNum-1));  
  47.  if(i == processNum -1)  
  48.   num = N - num * (processNum -2);  
  49.   ///gather the sorted data  
  50.   printf("Receiving from process %d... ", i);  
  51.   MPI_Recv(&dataArrB[(N/(processNum-1)) * (i-1)], num, MPI_INT, i, 55, MPI_COMM_WORLD, &mpistat);  
  52.   ///prepare for merge, set the pointers  
  53.   pointer[i] = (N/(processNum-1)) * (i-1);  
  54.   secEnd[i] = pointer[i] + N/(processNum-1);  
  55.   if(i == processNum-1) secEnd[i] = N;  
  56.   }  
  57.   printf("Sorted Sections Array: ");  
  58.   for(inti = 0;i< N; i++){  
  59.   printf("%d ", dataArrB[i]);  
  60.   }  
  61.   puts("");  
  62.   ///merge the sorted sections  
  63.   puts("Merging...");  
  64.   std::map<intint>data2rank;  
  65.   for(t = 1;t <processNum; t++){  
  66.   if(pointer[t] <secEnd[t]){  
  67.   data2rank.insert(std::make_pair<intint>(dataArrB[pointer[t]], t));  
  68.   pointer[t]++;  
  69.   }  
  70.   }  
  71.   for(inti = 0;i <N; i++){  
  72.   intdata = data2rank.begin()->first;  
  73.   intrank = data2rank.begin()->second;  
  74.   dataArr[i] = data;  
  75.   data2rank.erase(data2rank.begin());  
  76.   if(pointer[rank] <secEnd[rank])  
  77.   {  
  78.   data2rank.insert(std::make_pair<intint>(dataArrB[pointer[rank]], rank));  
  79.   pointer[rank]++;  
  80.   }  
  81.   }  
  82.   ///output the results  
  83.   printf("Final Sorted Array: ");  
  84.   for(inti = 0;i< N; i++){  
  85.   printf("%d ", dataArr[i]);  
  86.   }  
  87.   printf(" ");  
  88.   }  
  89.   else 
  90.   {  
  91.   //receieve the section  
  92.   MPI_Recv(&num, 1, MPI_INT, 0, 55, MPI_COMM_WORLD, &mpistat);  
  93.   MPI_Recv(&dataArr[0], num, MPI_INT, 0, 55, MPI_COMM_WORLD, &mpistat);  
  94.   printf("Received Original Array: ");  
  95.   for(inti = 0;i< num; i++){  
  96.  printf("%d ", dataArr[i]);  
  97.   }  
  98.   printf(" ");  
  99.   //sort this section  
  100.   qsort(dataArr, num, sizeof(int), QuickSortCompareFun);  
  101.   MPI_Send(&dataArr[0], num, MPI_INT, 0, 55, MPI_COMM_WORLD);  
  102.   ///display  
  103.   printf("My Sorted Section: ");  
  104.   for(inti = 0;i< num; i++){  
  105.   printf("%d ", dataArr[i]);  
  106.   }  
  107.   printf(" ");  
  108.   }  
  109.   MPI_Finalize();  
  110.  return0; 

原文链接:http://www.cnblogs.com/rosting/archive/2011/11/16/2251892.html

[[50028]]

【编辑推荐】

  1. 微软发布新版Windows 7及.NET 4软件开发工具包
  2. 详解.NET 4.0并行计算支持历史
  3. 详读.NET 4.0环境配置
  4. 详解.NET 4.0中异常处理方面的新特性
  5. 三方面诠释.NET 4.0的新特性
责任编辑:彭凡 来源: 博客园
相关推荐

2013-12-16 16:49:57

OpenMP

2013-12-16 16:58:47

OpenMP并行

2012-03-12 12:34:02

JavaF#

2010-06-03 19:28:02

Hadoop

2020-02-26 09:42:15

主存程序存储器

2016-03-28 10:00:09

Swift命令程序

2023-03-31 08:44:55

Go开发命令

2013-05-14 10:53:41

AIR Android真机运行

2019-04-16 06:50:34

2010-07-15 10:58:23

Perl命令行程序

2021-04-19 11:30:06

Java开发程序

2021-04-21 07:53:12

JavaScript单行程序

2012-04-26 13:36:30

iPhone运行程序

2010-02-26 13:03:31

Python脚本语言

2022-02-04 22:05:19

JVM程序内存模型

2013-05-14 10:41:31

2011-09-19 19:21:54

linux

2013-10-31 15:47:29

CloudaApp

2010-02-24 15:41:53

Python解释器

2015-07-15 10:32:44

Node.js命令行程序
点赞
收藏

51CTO技术栈公众号