使用MPI 实现奇偶排序

使用MPI 实现奇偶排序

0号进程获得待排序序列并输出排序好的序列

使用文件进行输入输出

进行性能测试与对比

代码

奇偶排序

头文件引入
#include <iostream>
#include <algorithm>
#include <mpi.h>
#include <fstream>
#include <chrono>
定义规模
#define N 100000000
int I[N];
计算partner
int Compute_partner(int phase, int my_rank, int comm_sz) {
	int partner;
	if (phase % 2 == 0) {  // 偶数阶段
		if (my_rank % 2 != 0) {
			partner = my_rank - 1;
		}
		else {
			partner = my_rank + 1;
		}
	}
	else {  // 奇数阶段
		if (my_rank % 2 != 0) {
			partner = my_rank + 1;
		}
		else {
			partner = my_rank - 1;
		}
	}
	if (partner < 0 || partner >= comm_sz) {
		partner = -1;  // No valid partner
	}
	return partner;
}
保留小一半数据
void Keep_smaller_keys(int* A, int* B, int n) {
	int* tmp = new int[n];
	int a = 0, b = 0, t = 0;
	while (t < n) {
		if (A[a] < B[b]) {
			tmp[t] = A[a];
			t++;
			a++;
		}
		else {
			tmp[t] = B[b];
			t++;
			b++;
		}
	}
	for (int i = 0; i < n; i++) {
		A[i] = tmp[i];
	}
}
保留大一半数据
void Keep_larger_keys(int* A, int* B, int n) {
	int* tmp = new int[n];
	int a = n - 1, b = n - 1, t = n - 1;
	while (t >= 0) {
		if (A[a] < B[b]) {
			tmp[t] = B[b];
			t--;
			b--;
		}
		else {
			tmp[t] = A[a];
			t--;
			a--;
		}
	}
	for (int i = 0; i < n; i++) {
		A[i] = tmp[i];
	}
}
主函数
int main() {
	int* A, * B;
	int comm_sz, my_rank;
	int n;
	MPI_Init(NULL, NULL);
	auto start = std::chrono::high_resolution_clock::now();	
	auto finish = std::chrono::high_resolution_clock::now();
	MPI_Comm_size(MPI_COMM_WORLD, &comm_sz);
	MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
	n = N / comm_sz;
	A = new int[n];
	B = new int[n];
	
  //文件读入
	if (my_rank == 0) {
		ifstream input("input.txt");
		for (int i = 0; i < N && input >> I[i]; i++);
		input.close();
		start = std::chrono::high_resolution_clock::now();
	}

	MPI_Scatter(I, n, MPI_INT, A, n, MPI_INT, 0, MPI_COMM_WORLD);

	sort(A, A + n);
	for (int phase = 0; phase < comm_sz; phase++) {
		int partner = Compute_partner(phase, my_rank, comm_sz);

		if (partner != -1) {
			MPI_Sendrecv(A, n, MPI_INT, partner, 0, B, n, MPI_INT, partner, 0, MPI_COMM_WORLD, MPI_STATUSES_IGNORE);
			if (my_rank < partner) {
				Keep_smaller_keys(A, B, n);
			}
			else {
				Keep_larger_keys(A, B, n);
			}
		}
	}

  MPI_Gather(A, n, MPI_INT, I, n, MPI_INT, 0, MPI_COMM_WORLD);
  
  //文件输出
	if (my_rank == 0) {
		finish = std::chrono::high_resolution_clock::now();
		std::chrono::duration<double> elapsed = finish - start;
		std::cout << "Elapsed time: " << elapsed.count() << " s\n" << std::endl;
		ofstream output("output.txt");
		for (int elem : I) {
			output << elem << " ";
		}
		output << endl;
		output.close();
	}

	MPI_Finalize();
	return 0;
}

随机数序列生成

使用python生成一个包含100000000个数字的文本文件

import random

def generate_random_numbers(count):
    # Generate random numbers and write to a file
    with open(f'input{count}.txt', 'w') as file:
        for _ in range(count):
            number = random.randint(1, 10*count)  # Random numbers between 1 and 100
            file.write(f"{number}\n")

if __name__ == "__main__":
		generate_random_numbers(pow(10,8))

测试代码

#define _CRT_SECURE_NO_WARNINGS

#include<iostream>
#include<fstream>
#include<algorithm>
#include<Windows.h>
#include<chrono>

using namespace std;

#define N 100000000
int I1[N], I2[N];

int main() {
    ifstream input1("C:\\Users\\Xavier\\Documents\\并行\\Project2\\Project2\\input.txt");
    for (int i = 0; i < N && input1 >> I1[i]; i++);
    input1.close();
    ifstream input2("C:\\Users\\Xavier\\Documents\\并行\\Project2\\Project2\\output.txt");
    for (int i = 0; i < N && input2 >> I2[i]; i++);
    input2.close();

    auto start = std::chrono::high_resolution_clock::now();
    sort(I1, I1 + N);
    auto finish = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> elapsed = finish - start;
    std::cout << "Elapsed time: " << elapsed.count() << " s\n" << std::endl;
    Sleep(5000);

    bool is_success = true;
    for (int i = 0; i < N; i++) {
        if (I1[i] == I2[i]) {
            if (i % (N/10) == 0) {
                // Update progress bar in the first line
                HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
                COORD pos = { 0, 0 }; // Go back to the start of the first line
                SetConsoleCursorPosition(hConsole, pos);
                printf("测试中[%.2lf%%]: ", i * 100.0 / N);
                int show_num = (i + 1) * 20 / N;
                for (int j = 0; j < show_num; j++) {
                    cout << "=";
                }
                for (int j = show_num; j < 20; j++) {
                    cout << " ";
                }
                cout << "|" << endl;

                cout << i + 1 << "st Number Pass" << endl;
                Sleep(100); // Adjust speed of the update
            }
        }
        else {
            is_success = false;
            cout << "No " << i + 1 << " failed" << endl;
            cout << I1[i] << " " << I2[i] << endl;
            break;
        }
    }

    if (is_success) {
        cout << "Success" << endl;
    }
    else {
        cout << "Test failed." << endl;
    }
    return 0;
}

可视化代码

利用matplot进行可视化

import matplotlib.pyplot as plt
import json

with open('test.json') as f:
    data = json.load(f)

# Extracting data for plotting
powers = [item['pow'] for item in data]
oddeven_values = [item['oddeven'] * 1000 for item in data]  # Convert to milliseconds
std_values = [item['std'] * 1000 for item in data]         # Convert to milliseconds

# Number of groups
n_groups = len(powers)

# Create plot
fig, ax = plt.subplots()
index = range(n_groups)
bar_width = 0.35
opacity = 0.8

rects1 = ax.bar(index, oddeven_values, bar_width, alpha=opacity, color='b', label='OddEven (ms)')

rects2 = ax.bar([p + bar_width for p in index], std_values, bar_width, alpha=opacity, color='r', label='Std (ms)')

ax.set_xlabel('Log(Number of elements)')
ax.set_ylabel('Time (ms)')
ax.set_title('Execution time comparison')
ax.set_yscale('log')  # Set logarithmic scale
ax.set_xticks([p + bar_width / 2 for p in index])
ax.set_xticklabels(powers)
ax.legend()

plt.tight_layout()
plt.show()

结果

test

在这里插入图片描述

因为我的电脑的CPU是I7-12700H,只有20个物理核心,所以在执行MPI并行程序时将线程设置为16。将16线程奇偶排序与串行Std::Sort在不同数据规模下用相同数据集进行测试,得到的结果如下(具体的数值记录在test.json文件中):
在这里插入图片描述

对于较小的数据集(小于等于 1 0 4 10^4 104个数字),并行奇偶排序的执行效率不如Std::Sort,但是随着数据规模增加,并行奇偶排序的优势开始体现,对于超过 1 0 5 10^5 105个数字的数据集,并行奇偶排序的效率明显更高,在数据规模来到 1 0 8 10^8 108时,16线程并行奇偶排序的用时是5.43495s,而串行Std::Sort的用时是42.4069s。随着规模增大,并行奇偶排序的优势将不断增加,并行特性使其能够更高效地处理较大的数据集。

遇到的问题及解决

C2131表达式的计算结果不是常数

vs2022禁止如下语句:

int a[n];	//n为变量

我们可以使用动态内存分配解决:

int *a=new int[n];

使用普通计时器精度过低

在数据规模较小时使用普通计时器无法记录程序运行时间,采用高精度计时器:

#include <chrono>
auto start = std::chrono::high_resolution_clock::now();
auto finish = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed = finish - start;
std::cout << "Elapsed time: " << elapsed.count() << " s\n"<<std::endl;

有未经处理的异常: 0xC00000FD: Stack overflow

爆栈了,可以在vs2022中手动设置堆栈保留大小或者使用动态内存分配

C2148: 数组的总大小不得超过0x7fffffff字节

在测试 1 0 9 10^9 109数据时报错,因此最终仅对 1 0 8 10^8 108规模的数据做测试

  • 36
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 使用 MPI 实现快速排序的一种方法是使用分治策略。首先,将数组划分成若干个子数组,然后使用 MPI 进程分别对各自的子数组进行快速排序。然后,在所有进程完成排序之后,使用 MPI 函数进行数据交换,将所有子数组中的元素进行归并。最后,对归并后的数组进行快速排序即可完成整个数组的排序。 下面是一个使用 MPI 实现快速排序的简单示例: ``` #include <stdio.h> #include <stdlib.h> #include <mpi.h> #define N 1000000 int cmp(const void *a, const void *b) { return (*(int *)a - *(int *)b); } int main(int argc, char *argv[]) { int rank, size; int i, pivot, temp; int *data, *sub_data; MPI_Status status; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &size); data = (int *)malloc(N * sizeof(int)); sub_data = (int *)malloc((N / size) * sizeof(int)); if (rank == 0) { for (i = 0; i < N; i++) data[i] = rand(); } MPI_Scatter(data, N / size, MPI_INT, sub_data, N / size, MPI_INT, 0, MPI_COMM_WORLD); qsort(sub_data, N / size, sizeof(int), cmp); if (rank != 0) { MPI_Send(sub_data, N / size, MPI_INT, 0, 0, MPI_COMM_WORLD); } else { for (i = 1; i < size; i++) { MPI_Recv(data, N / size, MPI_INT, i, 0, MPI_COMM_WORLD, &status); qsort(data, N / size, sizeof(int), cmp); } qsort(sub ### 回答2: 快速排序是一种常用的排序算法,在并行计算中可以利用MPI实现更高效的排序。 快速排序的基本思想是通过选定一个基准元素,将待排序序列切分成两个子序列,其中一个序列所有元素都小于基准元素,另一个序列所有元素都大于基准元素。然后对两个子序列分别进行快速排序,最终将两个有序序列合并为一。 在MPI中,我们可以利用分布式内存模型,将待排序序列分布到不同的进程上,每个进程负责排序一部分数据。首先,我们选择一个进程作为主进程,负责生成随机序列,并将序列拆分并发送给其他进程。然后,每个进程对接收到的数据进行快速排序排序结束后,他们将排好序的数据发送给主进程。 在主进程接收到所有排序结果后,可以利用归并操作将多个有序序列合并为一个有序序列,最终得到完整的有序序列。 在实际实现中,需要注意处理边界情况,比如仅有一个进程的情况,此时直接对整个序列进行排序即可。另外,在切分序列并选择基准元素时,需要使用一些算法来提高快速排序的效率,比如选择中位数作为基准元素。 总而言之,通过MPI实现快速排序可以利用多个进程同时处理不同部分的数据,并利用分布式计算资源加速排序过程,从而实现更快速的排序。 ### 回答3: 使用MPI(Message Passing Interface)实现快速排序需要将排序任务划分为多个子任务,并通过消息传递机制协同各个进程进行排序。 首先,选择一个排序算法作为快速排序的基本算法。快速排序的基本思想是通过选择一个基准元素,将待排序序列划分为两个子序列,左侧序列小于等于基准元素,右侧序列大于基准元素,然后分别对左右子序列进行递归排序。 在MPI实现中,可以采用Master-Worker模型。首先,Master进程负责读入待排序序列,并将序列拆分成若干个均匀的子序列,将每个子序列分发给Worker进程。Master进程通过发送消息给Worker进程来分配子序列和指示排序操作。 每个Worker进程接收子序列后,使用快速排序对其进行排序排序过程中,Worker进程将子序列根据基准元素的大小分为两个子序列,并将左右子序列分别发送给其他Worker进程进行排序。这样不断地划分和排序,直到子序列长度为1或为空,则排序完成。 排序完成后,每个Worker进程将自己的排序好的子序列发送回Master进程。Master进程接收到Worker进程返回的消息后,按照顺序将这些子序列合并起来,得到最终的有序序列。 最后,Master进程将有序序列输出,即得到了使用MPI实现的快速排序结果。 需要注意的是,MPI实现快速排序的过程中需要合理地划分子序列,并进行消息传递和接收。同时,应确保MPI进程之间的通信是同步的,以保证排序的正确性和一致性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值