并行计算 利用mpi实现PSRS算法

输入:nn个待排序的数据,PP个处理器。
输出:从小到大排序的数据。
算法思想:PP个处理器对自己的n/Pn/P个数据进行局部排序。每个处理器从局部有序的序列中选出PP个数据作为自己的代表元素并将它们送到P0P_0中,P0P_0对这P2P^2个元素进行排序,然后从中选取P1P-1个主元并将它们广播到所有的处理器中。然后每个处理器根据这P1P-1个主元把自己的局部有序序列划分成PP个部分,把每个部分送给对应的处理器,然后再对自己收到的部分做PP路归并排序,最后把它们放到数组的对应位置上即可。
环境:VS2019VS2019
codecode:

#include<cstdio>
#include<cmath>
#include<mpi.h>
#include<ctime>
#include<vector>
#include<iostream>
#include<algorithm>
#include<queue>
#define pr pair<int,int>
using namespace std;

inline void quicksort(int* vec, int beg, int end, int* pivots, int numprocs, int w)	//对给定区间的元素进行排序 并选取代表元素
{
	sort(vec+beg, vec + end);	//排序
	for (int i = 0; i < numprocs; i++)	//选取代表元素
	{
		pivots[i] = vec[beg + i * w];
	}
}

inline void AnoSort(int* src, int* dst, int* recvcount, int* rdispls, int numprocs,int totalnum)
{
	int cur = 0, MIN, idx;
	vector<int> cnt(numprocs);
	while (cur < totalnum)
	{
		idx = -1;
		for (int i = 0; i < numprocs; i++)
		{
			if (cnt[i] != recvcount[i])
			{
				if (idx == -1 || src[cnt[i] + rdispls[i]] < MIN)
					MIN = src[cnt[i] + rdispls[i]], idx = i;
			}
		}
		dst[cur++] = MIN;
		++cnt[idx];
	}
}

inline void MergeSort(int* src, int* dst, int* recvcount, int* rdispls, int numprocs)	//另外一种多路归并排序
{
	priority_queue<pr, vector<pr>, greater<pr>> q;//first 为具体的值 second为row的下标
	vector<int> idx(numprocs);
	for (int i = 0; i < numprocs; i++)
	{
		if (idx[i] != recvcount[i])
		{
			q.push(pr(src[idx[i] + rdispls[i]], i));
			++idx[i];
		}
	}
	int cur = 0;
	pr tmp;
	while (!q.empty())
	{
		tmp = q.top();
		q.pop();
		dst[cur++] = tmp.first;
		if (idx[tmp.second] != recvcount[tmp.second])
		{
			tmp.first = src[idx[tmp.second] + rdispls[tmp.second]];
			++idx[tmp.second];
			q.push(tmp);
		}
	}
}

inline void choosepivots(int* pivots, int numprocs, int myid,int *final_pivots)	//选取主元并广播
{
	int* root_pivots = new int[numprocs * numprocs];;	//收集pivots数组
	MPI_Gather(pivots, numprocs, MPI_INT, root_pivots, numprocs, MPI_INT, 0, MPI_COMM_WORLD);
	if (myid == 0)		//0号进程
	{
		sort(root_pivots, root_pivots + numprocs * numprocs);
		for (int i = 1; i < numprocs; i++)	//选择排序后的第P-1,2(P-1),…,(P-1)(P-1)个共P-1个主元;
			final_pivots[i - 1] = root_pivots[i * numprocs];
	}
	MPI_Bcast(final_pivots, numprocs - 1, MPI_INT, 0, MPI_COMM_WORLD);	//将最终选取的主元广播出去
	delete[] root_pivots;
}

inline void PSRS(int* vec, int n)
{
	int myid, numprocs;
	MPI_Comm_rank(MPI_COMM_WORLD, &myid);	//得到进程id
	MPI_Comm_size(MPI_COMM_WORLD, &numprocs);	//进程总数
	//if (!myid)
	//{
	//	cout << "初始数组:\n";
	//	for (int i = 0; i < n; i++)
	//		cout << vec[i] << ' ';
	//	cout << "\n";
	//}
	clock_t start = clock();

	int num = n / numprocs;	//每个处理器要处理的数据
	int beg = myid * num;	//区间起始位置
	int end = (myid + 1) * num;	//区间结束位置 左闭右开
	int w = n / (numprocs * numprocs);	//选取代表元素
	end = min(end, n);	//限制区间右端点的最大值
	int* pivots = new int[numprocs];
	quicksort(vec, beg, end, pivots, numprocs, w);	//进行局部排序 并选取代表元素
	if (numprocs <= 1)	//仅有1个进程
	{
		clock_t end = clock();
		//cout << "排完序后:\n";
		//for (int i = 0; i < n; i++)
		//	cout << myarray[i] << ' ';
		cout << end - start << endl;
		delete[] pivots;
		return;
	}
	int* final_pivots = new int[numprocs - 1];
	choosepivots(pivots, numprocs, myid, final_pivots);	//选取主元并广播

	delete[] pivots;

	int* sendcount = new int[numprocs], * sdispls = new int[numprocs];	//记录分割后 每一段的个数 全局交换需要用到
	int* recvcount = new int[numprocs], * rdispls = new int[numprocs];

	sdispls[0] = beg;
	sendcount[0] = upper_bound(vec + beg, vec + end, final_pivots[0]) - vec - sdispls[0];
	for (int i = 1; i < numprocs - 1; i++)
	{
		sdispls[i] = sdispls[i - 1] + sendcount[i - 1];		//记录每一段发送的起始位置
		sendcount[i] = upper_bound(vec + beg, vec + end, final_pivots[i]) - vec - sdispls[i];//计算每一段发送的个数
	}
	sdispls[numprocs - 1] = sdispls[numprocs - 2] + sendcount[numprocs - 2];
	sendcount[numprocs - 1] = end - sdispls[numprocs - 1];
	delete[] final_pivots;

	MPI_Alltoall(sendcount, 1, MPI_INT, recvcount, 1, MPI_INT, MPI_COMM_WORLD);	//记录每一段要接受的个数
	int totalnum = recvcount[0];
	rdispls[0] = 0;
	for (int i = 1; i < numprocs; i++)
	{
		totalnum += recvcount[i];
		rdispls[i] = rdispls[i - 1] + recvcount[i - 1];
	}
	int* result = new int[totalnum];
	MPI_Alltoallv(vec, sendcount, sdispls, MPI_INT, result, recvcount, rdispls, MPI_INT, MPI_COMM_WORLD);	//全局交换
	int* sort_result = new int[totalnum];
	//MergeSort(result, sort_result, recvcount, rdispls, numprocs);	//多路归并
	AnoSort(result, sort_result, recvcount, rdispls, numprocs, totalnum);
	int* num_idx = new int[numprocs];	//存储每个进程控制的元素数
	MPI_Gather(&totalnum, 1, MPI_INT, num_idx, 1, MPI_INT, 0, MPI_COMM_WORLD);	//汇集信息到根进程中
	int* finalpos = new int[numprocs];
	if (myid == 0)
	{
		finalpos[0] = 0;
		for (int i = 1; i < numprocs; i++)
		{
			finalpos[i] = finalpos[i - 1] + num_idx[i - 1];	//最终放置的位置
		}
	}
	MPI_Gatherv(sort_result, totalnum, MPI_INT, vec, num_idx, finalpos, MPI_INT, 0, MPI_COMM_WORLD);
	if (!myid)
	{
		clock_t end = clock();
		//cout << "排完序后:\n";
		//for (int i = 0; i < n; i++)
		//	cout << vec[i] << ' ';
		//cout << endl;
		cout << end - start << endl;
	}
	delete[] num_idx;
	delete[] finalpos;
	delete[] result;
	delete[] sort_result;
	delete[] sendcount;
	delete[] sdispls;
	delete[] recvcount;
	delete[] rdispls;
}

int main(int argc, char* argv[])
{
	int n = 1e7;		//元素个数
	int* vec = new int[n];
	srand(time(0));
	for (int i = 0; i < n; i++)
	{
		//vec[i] = i + 1;
		vec[i] = rand();	//数组
	}
	MPI_Init(&argc, &argv);
	PSRS(vec, n);	//排序
	MPI_Finalize();

	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章