首页 > 其他分享 >使用MPI 实现奇偶排序

使用MPI 实现奇偶排序

时间:2024-06-22 14:56:46浏览次数:23  
标签:std 奇偶 int rank MPI partner 排序 my

使用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规模的数据做测试

标签:std,奇偶,int,rank,MPI,partner,排序,my
From: https://blog.csdn.net/qq_42426268/article/details/139791657

相关文章

  • Python 冒泡排序
    冒泡排序是一种简单的排序算法,它重复地遍历要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。遍历数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。以下是一个用Python实现的冒泡排序算法的例子:pythondefbubble_sort(lst):n=len......
  • 高性能并行计算华为云实验一:MPI矩阵运算
    目录一、实验目的二、实验说明三、实验过程3.1创建矩阵乘法源码3.1.1实验说明3.1.2实验步骤3.2创建卷积和池化操作源码3.2.1实验说明3.2.2实验步骤3.3创建Makefile文件并完成编译3.4建立主机配置文件与运行监测四、实验结果与分析4.1矩阵乘法实验4.1.1......
  • 随机链表的复制 && 排序链表
    随机链表的复制题目.-力扣(LeetCode)思路:思路:       ①一个结点一个节点去拷贝,当拷贝了第一个节点的时候,把原节点与拷贝节点连接起来,直接到所有的节点拷贝完毕,这样做的目的是为下一步处理random指针做准备      ②处理random       ③处理......
  • 手撕排序2--选择排序(直接选择+堆排序
    目录:1.直接选择排序  的实现及分析2.堆排序的实现及分析1.直接选择排序1.1基本思想:每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。1.2一次排序-->将最大值放在第一个,最小值放在最后一个代码实现:#incl......
  • 校招常见七大排序C++版(适合新人,通俗易懂)
    作者:求一个demo版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处内容通俗易懂,没有废话,文章最后是面试常问内容是否稳定最优、最坏、平均时间复杂度最优、最坏、平均空间复杂度冒泡排序是O(n)、O(n^2)、O(n^2)0、O(n)、O(1)选择排序否O(n^2)、O(n^2)......
  • P8500 [NOI2022] 冒泡排序 题解
    考虑特殊性质B。限制相当于钦定一些位置的值,其他位置无限制。可以发现性质:无限制的位置上填的值是单调不减的。证明:设得到的最优序列为\(A\),对于无限制的位置\(i,j\),若\(A_i>A_j\),交换\(i,j\)后逆序对个数必然减小。根据改性质,只需考虑每个位置对已经确定位置的位置的贡......
  • 4.插入排序
    插入排序插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。排序思路:假设按照升序排序1.从索引为1的元素开始向前比较,一旦前面一个元素大于自己就让前面的元素......
  • 算法题---五个线程排序输出
    1、五个线程编号1、2、3、4、5,每个线程的执行完成时间不确定,要求按照排号顺序输出各个线程的结果,并且不能等所有线程执行完毕再排序输出,比如线程2先于线程1执行完了此时还不能输出。要等线程1输出完之后才能输出,其他线程以此类推方案一、利用所得传递,创建五把锁lock1、2、3、4......
  • 堆排序|维护堆和建堆
    堆可以看作各个元素之前有前后续的特殊数组,当然也是一颗完全二叉树。设堆heap的元素为heap[1,2,3,...,heap_size]。注意0<=heap_size<=heap.len,并且节点从1开始计数(便于计算)。1.寻找节点1.1寻找父节点若节点编号为i......
  • 冒泡排序程序
    #include<stdio.h>voidbubbleSort(intarr[],intsize){for(inti=0;i<size-1;i++){for(intj=0;j<size-1-i;j++)//每次将最大值放到最后所以会少i{if(arr[j]>arr[j+1]){......