《并行程序设计导论》第三章:
# 用MPI进行分布式内存编程-教程05
###并行归并排序参考答案:
/* File:
* parallelMerge.c
*
* Author:
* E2MCC
*
* Discription:
* implement merge sort by using parallel program.
*
* Input:
* in.txt
*
* Output:
* out.txt
*/
#include
#include
#include
#include
/*---Set Global Variables below-------------------------------*/
int my_rank = 0;
int comm_sz = 0;
MPI_Comm comm;
int keys_n = 0;//the totall number of input keys
float* keys_p = NULL;//only can be set by proc 0
int local_keys_n = 0;//the local keys number
float* local_keys_p = NULL;//the local keys
double local_time_start = 0;
double local_time_finish = 0;
double local_time_elapsed = 0;
double time_elapsed = 0; //only can be set by proc 0
/*---Set Global Variables above-------------------------------*/
void readKeys(void);
void scatterKeys(void);
void mergeSort(float* array, int l, int mid, int h);
void merge(float* array, int l, int h);
void pMergeSort(float* recv_keys_p,int recv_keys_n);
void getDestAndSource(int* dest_p,int* source_p,int local_peralleled);
void getSendOrRecv(int* send_or_recv_p,int proc_n,int new_rank);
void parallelMerge(void);
void output(void);
int main(int argc,int** argv){
/*init MPI and proc relativable variable*/
MPI_Init(NULL,NULL);
comm = MPI_COMM_WORLD;
MPI_Comm_size(comm,&comm_sz);
MPI_Comm_rank(comm,&my_rank);
/*--------------------------------------*/
//input keys
readKeys();
/*---start time-------------------------*/
MPI_Barrier(comm);
local_time_start = MPI_Wtime();
/*---------Scatter keys-----------------*/
scatterKeys();
/*---------Merge------------------------*/
parallelMerge();
/*----------time----------------------*/
local_time_finish = MPI_Wtime();
local_time_elapsed = local_time_finish-local_time_start;
MPI_Reduce(&local_time_elapsed,&time_elapsed,1,MPI_DOUBLE,MPI_MAX,0,comm);
if(my_rank==0){
printf("the elapsed time is %e\n",time_elapsed);
}
output();
MPI_Finalize();
return 0;
}
/*input the original keys*/
void readKeys(void){
if(my_rank==0){
printf("RANK-%d of %d--->I will input keys from in.txt right now!\n",my_rank,comm_sz);
FILE* file_p = NULL;
file_p = fopen("in.txt","r");
fscanf(file_p,"%d",&keys_n);
keys_p = malloc(keys_n*sizeof(float));
printf("Successfully set the keys_n = %d:\n",keys_n);
int i = 0;
for(i=0;i<keys_n;i++){
fscanf(file_p,"%f",&(keys_p[i]));
}
fclose(file_p);
printf("Read Keys Finished!\n");
}
MPI_Bcast(&keys_n,1,MPI_INT,0,comm);//let every process konw
}
//Scatter keys to all procs that is the in communicator
void scatterKeys(void){
int quotient = keys_n/comm_sz;
int remainder = keys_n%comm_sz;
if(my_rank<remainder){
local_keys_n = quotient+1;
}else{
local_keys_n = quotient;
}
local_keys_p = malloc(local_keys_n*sizeof(float));
int* send_count_p = NULL;//array of send_count
int* displs_p = NULL;//array of displs
if(my_rank==0){
send_count_p = malloc(comm_sz*sizeof(int));
displs_p = malloc(comm_sz*sizeof(int));
int offset = 0;
int i = 0;
for(i=0;i<comm_sz;i++){
if(i<remainder){
send_count_p[i] = quotient+1;
}else{
send_count_p[i] = quotient;
}
displs_p[i] = offset;
offset = offset+send_count_p[i];
}
}
MPI_Scatterv(keys_p,send_count_p,displs_p,MPI_FLOAT,
local_keys_p,local_keys_n,MPI_FLOAT,0,comm);
if(my_rank==0){
free(send_count_p);
free(displs_p);
}
}
void mergeSort(float* array, int l, int mid, int h){
//create 2 temp arrays to store keys
int temp1_n = mid - l + 1;
int temp2_n = h - mid;
float* temp1 = malloc(temp1_n*sizeof(float));
float* temp2 = malloc(temp2_n*sizeof(float));
int i = 0;
int j = 0;
int k = l;
for(i=0; i < temp1_n; i++){
temp1[i] = array[l+i];
}
for(j=0; j < temp2_n; j++){
temp2[j] = array[mid+1+j];
}
i=0;
j=0;
while(i<temp1_n&& j<temp2_n){
//copy the miner one
if(temp1[i] <= temp2[j]){
array[k] = temp1[i];
i++;
k++;
}else{
array[k] = temp2[j];
j++;
k++;
}
}
/*if there still are keys,just put them into array[] one by one*/
while(i < temp1_n){
array[k] = temp1[i];
i++;
k++;
}
while(j < temp2_n){
array[k] = temp2[j];
j++;
k++;
}
// free temp1 and temp2
if (temp1!=NULL){
free(temp1);
}
if(temp2!=NULL){
free(temp2);
}
}
void merge(float* array, int l, int h){
if(l<h){
int mid = (l+h)/2;
merge(array,l,mid);
merge(array,mid+1,h);
mergeSort(array,l,mid,h);
}
}
void pMergeSort(float* recv_keys_p,int recv_keys_n){
int temp_n = local_keys_n+recv_keys_n;
float* temp_p = malloc(temp_n*sizeof(float));
int i = 0;
int j = 0;
int k = 0;
while(i<local_keys_n&&j<recv_keys_n){
if(local_keys_p[i]<=recv_keys_p[j]){
temp_p[k] = local_keys_p[i];
i++;
k++;
}else{
temp_p[k] = recv_keys_p[j];
j++;
k++;
}
}
while(i<local_keys_n){
temp_p[k] = local_keys_p[i];
i++;
k++;
}
while(j<recv_keys_n){
temp_p[k] = recv_keys_p[j];
j++;
k++;
}
//update local_keys
local_keys_n = temp_n;
float* f = local_keys_p;
local_keys_p = temp_p;
free(f);
}
/*----------------------------------------------------------------*/
/*the process who calls this function get his partner rank including dest and source partner rank*/
void getDestAndSource(int* dest_p /*out*/,
int* source_p /*out*/,
int local_peralleled /*in*/){
*source_p = MPI_ANY_SOURCE;
int interval = 1;//set the interval between 2 adjacent process
interval<<=local_peralleled;
*dest_p = my_rank-interval;
if(*dest_p<0){
if(my_rank!=0){
*dest_p = 0;
}else{
*dest_p = MPI_PROC_NULL;
}
}
}
/*---------------------------------------------------------------*/
/*--------------------------------------------------------------*/
/*function:should this proc send or recieve data? 0 means recv;1 means send*/
void getSendOrRecv(int* send_or_recv_p /*out*/,
int proc_n /*in*/,
int new_rank /*in*/){
if(proc_n%2==0){//if communicator has even process
if(new_rank%2==1){//if new_rank is odd
*send_or_recv_p = 1;//this process should send
}else{
send_or_recv_p = 0;//this process should recv
}
}else{
if(new_rank%2==1){
*send_or_recv_p = 0;//should recv
}else{
*send_or_recv_p = 1;//should send
}
}
}
/*------------------------------------------------------------*/
void parallelMerge(void){
int recv_keys_n = 0;
float* recv_keys_p = NULL;
/*func:At first , merge process's local-keys*/
merge(local_keys_p,0,local_keys_n-1);
int dest = 0;
int source = 0;
int send_or_recv = 0;//0 means recv;1 means send
//how many times this process who calls parallelMerge has paralleled
int local_paralleled = 0;
/*--set new rank--*/
int new_rank = my_rank;
int quotient = 0;
int remainder = 0;
int sleep = 0;
int proc_n = comm_sz;
while(proc_n>1){
if(my_rank==0){
if(proc_n%2==0){
sleep=0;
}else{
sleep=1;
}
}
if(sleep==0){
getSendOrRecv(&send_or_recv,proc_n,new_rank);
getDestAndSource(&dest,&source,local_paralleled);
if(send_or_recv==1){
MPI_Send(&local_keys_n,1,MPI_INT,dest,0,comm);
MPI_Send(local_keys_p,local_keys_n,MPI_FLOAT,dest,1,comm);
sleep = 1;
}else{
MPI_Recv(&recv_keys_n,1,MPI_INT,source,0,comm,MPI_STATUS_IGNORE);
recv_keys_p = malloc(recv_keys_n*sizeof(float));
MPI_Recv(recv_keys_p,recv_keys_n,MPI_FLOAT,source,1,comm,MPI_STATUS_IGNORE);
pMergeSort(recv_keys_p,recv_keys_n);
free(recv_keys_p);
local_paralleled++;
}
}
if(my_rank==0){
if(proc_n%2==0){
proc_n = proc_n/2;
}else{
proc_n = (proc_n-1)/2+1;
}
}
MPI_Bcast(&proc_n,1,MPI_INT,0,comm);
/*it's time to renew the new_rank after we just renew the proc_n*/
quotient = new_rank/2;
remainder = new_rank%2;
new_rank = quotient+remainder;
}
}
void output(void){
if(my_rank==0){
FILE* fp = NULL;
fp = fopen("out.txt","w+");
int i = 0;
for(i=0;i<local_keys_n;i++){
fprintf(fp,"%.1f ",local_keys_p[i]);
}
fclose(fp);
}
}