MPI编程实例(PI、AllGather、排序)

5 篇文章 0 订阅
4 篇文章 0 订阅

1.MPI 简介

MPI(Message Passing Interface)是目前最重要的一个基于消息传递的并行编程工具,它具有移植性好、功能强大、效率高等许多优点,而且有多种不同的免费、高效、实用的实现版本,几乎所有的并行计算机厂商都提供对它的支持,成为了事实上的并行编程标准。

MPI是一个库,而不是一门语言,因此对MPI的使用必须和特定的语言结合起来进行。MPI不是一个独立的自包含系统,而是建立在本地并行程序设计环境之上,其进程管理和I/O均由本地并行程序设计环境提供。例如,MPI可以建立在IBM SP2的POE/MPL之上,也可以建立在Intel Paragon的OSF/NX。除了这些商业版本的MPI实现,还有一些免费版的MPI实现,主要有MPICH,LAM和CHIMP。

2.实验环境

本文是在KD机群上跑的实验,在Windows下也可搭建MPI运行环境,本文在这里不做详细介绍。

编译mpi程序: mpicc demo.c –o demo.o;运行mpi程序: mpirun -np 4 ./demo.o

3.实例

(1)用MPI编程实现PI的计算。

算法描述:键盘输入步骤次数 n,并把 n 广播给本通信环境中的所有进程,通过MPI_Bcast(&n, 1, MPI_INT, 0, MPI_COMM_WORLD); 各个进程计算自己的mypi = width * sum; 通过 MPI_Reduce(&mypi,  &pi,  1,  MPI_DOUBLE,  MPI_SUM,  0, MPI_COMM_WORLD);由进程 0 进行归约,把每个进程计算出来的 mypi 进行相加(MPI_SUM),赋给pi 。

#include<stdio.h>
#include<mpi.h>
#include<math.h>

int main(int argc, char *argv[]){
     int my_rank, num_procs;
     int i, n = 0;
     double sum, width, local, mypi, pi;
     double start = 0.0, stop = 0.0;
     int proc_len;
     char processor_name[MPI_MAX_PROCESSOR_NAME];
     MPI_Init(&argc, &argv);
     MPI_Comm_size(MPI_COMM_WORLD,  &num_procs);
      MPI_Comm_rank(MPI_COMM_WORLD,  &my_rank);
     MPI_Get_processor_name(processor_name,  &proc_len);
     printf("Process %d of %d\n", my_rank, num_procs);
     if(my_rank == 0){
         printf("please give step number n:");
         scanf("%d", &n);
         printf("\n");
         start = MPI_Wtime();
     }
 //  printf("Process %d of %d\n", my_rank, num_procs);
     MPI_Bcast(&n, 1, MPI_INT, 0, MPI_COMM_WORLD);
     width = 1.0 / n;
      sum = 0.0;
     for(i = my_rank; i < n; i += num_procs){
         local = width * ((double)i + 0.5);
         sum += 4.0 / (1.0 + local * local);
     }
     mypi = width * sum;
     MPI_Reduce(&mypi,  &pi,  1,  MPI_DOUBLE,  MPI_SUM,  0,
             MPI_COMM_WORLD);
     if(my_rank == 0){
         printf("PI is %.20f\n", pi);
         stop = MPI_Wtime();
         printf("Time: %f on %s\n", stop-start, processor_name);
         fflush(stdout);
     }
     MPI_Finalize();
     return 0;
}


(2)采用MPI_SEND和MPI_RECV编写代码来实现MPI_Allgather的功能,并写一个完整的MPI程序测试并对比实现和MPI原有实现的性能。

算法描述:对每一个进程pi,使用MPI_Send向其他所有进程发送消息,同时使用MPI_Recv从其他所有进程接收消息。

#include<stdio.h>
#include<stdlib.h>
#include"mpi.h"
#include<memory.h>

void MPI_Allgather_my(int * senddata, int sendcount, MPI_Datatype senddatatype, int * recvdata, int recvcount, MPI_Datatype recvdatatype, MPI_Comm comm)
{
	int rank, size, i;
	MPI_Request request;
	MPI_Status status;
	MPI_Comm_rank(MPI_COMM_WORLD, &rank);
	MPI_Comm_size(MPI_COMM_WORLD, &size);
	for(i = 0; i < size; i++)
	{
		if(i != rank)
		{
			MPI_Send( senddata, sendcount, senddatatype, i, 1, MPI_COMM_WORLD );
			MPI_Recv( recvdata + i*recvcount, recvcount, recvdatatype, i, 1, MPI_COMM_WORLD, &status );
		}
	}
	memcpy(recvdata+rank*recvcount,senddata,sizeof(senddatatype)*sendcount);
}

int main(int argc, char* argv[])
{
	int i, rank, size, tag = 1;
	int senddata[500] = {1}, recvdata[500*32];
	double start_time, end_time, s_t, e_t;
	int count = 500;
	MPI_Init(&argc, &argv);
	MPI_Comm_rank(MPI_COMM_WORLD, &rank);
	MPI_Comm_size(MPI_COMM_WORLD, &size);
	start_time = MPI_Wtime();
	MPI_Allgather_my(senddata, count, MPI_INT, recvdata, count, MPI_INT, MPI_COMM_WORLD);
	end_time = MPI_Wtime();	
	MPI_Reduce(&start_time, &s_t, 1, MPI_DOUBLE, MPI_MIN, 0, MPI_COMM_WORLD);
	MPI_Reduce(&end_time, &e_t, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD);
	if(rank == 0)
	{
		printf("myallgather : count = %d total time = %f\n", count, e_t - s_t);
	}
	MPI_Barrier(MPI_COMM_WORLD);
	
	start_time = MPI_Wtime();
	MPI_Allgather(senddata, count, MPI_INT, recvdata, count, MPI_INT, MPI_COMM_WORLD);
	end_time = MPI_Wtime();	
	MPI_Reduce(&start_time, &s_t, 1, MPI_DOUBLE, MPI_MIN, 0, MPI_COMM_WORLD);
	MPI_Reduce(&end_time, &e_t, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD);
	if(rank == 0)
	{
		printf("allgather : count = %d total time = %f\n", count, e_t - s_t);
	}
	MPI_Finalize();
	return 0;
}


(3)利用MPI实现一个大数据量的排序算法。

算法描述:使用MPI_Send 和MPI_Receive 来进行数据的通信,使用MPI_Send 函数将数据分成n 个部分传送给n 个进程(n 为通信域Comm 包括的进程个数),然后各个
进程对各自所含数据进行快速排序,再使用MPI_Receiv 函数将各个进程都排好的数据传送给进程0,最后在进程0 中进行快排序。

 #include <stdio.h>
 #include <sys/time.h>
 #include <stdlib.h>
 #include <time.h>
 #include <mpi.h>
 #define length 1000000

 void swap(int *data, int i, int j) {
  int temp = data[i];
  data[i] = data[j];
  data[j] = temp;
 }

 int partition(int *data, int start, int end) {
     if (start >= end) return 0;
     int pivotValue = data[start];
     int low = start;
     int high = end - 1;
     while (low < high) {
         while (data[low] <= pivotValue && low < end) low++;
         while (data[high] > pivotValue && high > start) high--;
         if (low < high) swap(data, low, high);
     }
     swap(data, start, high);
     return high;
 }

 void quicksort(int *data, int start, int end) {
     if (end-start+1 < 2) return;
     int pivot = partition(data, start, end);
     quicksort(data, start, pivot);
     quicksort(data, pivot+1, end);
 }

 int main(int argc, char *argv[]) {
     MPI_Init(&argc, &argv);
     int rank, size;
     MPI_Comm_rank (MPI_COMM_WORLD, &rank);
     MPI_Comm_size (MPI_COMM_WORLD, &size);
     srand(time(0));
     int *data = (int*)malloc(sizeof(int)*length);
     int i;
     for (i=0; i<length/size; i++)
         data[i] = rand();
     MPI_Status status;
     if (rank == 0) {
         for (i=1; i<size; i++)
             MPI_Recv(data+i*length/size, length/size, MPI_INT, i, MPI_ANY_TAG, MPI_COMM_WORLD, &status);
     }
     else {
         MPI_Send(data,  length/size,  MPI_INT,  0,  0,  MPI_COMM_WORLD);
     }

     struct timeval start, end;
     gettimeofday(&start, 0);
     int s;
     int localDataSize =  length;
     int pivot;
     for (s=size; s > 1; s /= 2) {
         if (rank % s == 0) {
             pivot = partition(data, 0, localDataSize);
             MPI_Send(data+pivot,  localDataSize  -  pivot,MPI_INT, rank + s/2, 0, MPI_COMM_WORLD);
             localDataSize = pivot;
         }
         else if (rank % s == s/2) {
             MPI_Recv(data,  length,  MPI_INT,  rank  -  s/2,
                     MPI_ANY_TAG, MPI_COMM_WORLD, &status);
             MPI_Get_count(&status,  MPI_INT,
                     &localDataSize);
         }
     }
     quicksort(data, 0, localDataSize);
     gettimeofday(&end, 0);
     if (rank == 0)
     {
         float  time  =  (end.tv_sec  -  start.tv_sec)  +
             0.000001*(end.tv_usec - start.tv_usec);
         printf("Time: %f s\n", time);
     }
     MPI_Finalize();
     return 0;
}


4.小结

陈国良等.并行算法实践[M].北京:高等教育出版社,2004.1.

  • 10
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值