矩阵乘法的MPI并行实验报告

这篇具有很好参考价值的文章主要介绍了矩阵乘法的MPI并行实验报告。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

矩阵乘法的MPI并行实验报告

一、实验要求:

(1) 分别用 1,2,4,8 个进程完成矩阵乘法(同一个程序):A * B = C,其中 A,B,C 均为 2048*2048 双精度点方阵,0号进程负责初始化矩阵 A,B 并将结果存入 0 号进程。
(2) 绘制加速比曲线;

二、实验环境:

  • 操作系统:Windows11
  • 编程语言:C++(使用MPI接口)
  • 编译器:VC++
  • 核心库:MPI(MSMPI)
  • 编程工具:Visual Studio 2022
  • CPU:AMD Ryzen 7 6800H with Radeon Graphics 3.20 GHz
  • 内存:16GB

三、实验内容:

1. 实现思路

将可用于计算的进程数分解为a*b,然后将全体行划分为a个部分,全体列划分为b个部分,从而将整个矩阵划分为相同的若干个块。每个子进程负责计算最终结果的一块,只需要接收A对应范围的行和B对应范围的列,而不需要把整个矩阵传过去。主进程(0号进程)负责分发和汇总结果。
注意:
(1) 为了保证平均划分,矩阵需要扩展,即扩展至负责计算的进程数的倍数,扩展部分数据置为0,以保证结果准确性。
(2) 通信接口使用Send/Recv,所以进程编号要管理好。另外,主进程只负责初始化矩阵及分发和汇总结果。

2. 实验结果

(1) 设置为单进程,即串行

  • 单进程命令行参数设置(如图1)
    矩阵乘法的MPI并行实验报告
    图 1 单进程时命令行参数设置
  • 单进程运行结果,约97.20s(如图2)
    矩阵乘法的MPI并行实验报告图 2 单进程时运行的时间花销
    (2) 设置为2进程
  • 2进程命令行参数设置(如图3)
    矩阵乘法的MPI并行实验报告图 3 程序2进程运行时命令行参数设置
  • 2进程运行结果,约75.43s(如图4)矩阵乘法的MPI并行实验报告图 4 程序2进程时运行的时间花销
    (3) 设置为4进程
  • 4进程命令行参数设置(如图5)
    矩阵乘法的MPI并行实验报告图 5 程序4进程运行时命令行参数设置
  • 4进程运行结果,约17.17s(如图6)
    矩阵乘法的MPI并行实验报告图 6 程序4进程时运行的时间花销
    (4) 设置为8进程
  • 8进程命令行参数设置(如图7)
    矩阵乘法的MPI并行实验报告图 7 程序8进程运行时命令行参数设置
  • 8进程运行结果,约9.05s(如图8)
    矩阵乘法的MPI并行实验报告图 8 程序4进程时运行的时间花销
    由上述运行结果可得表格1
    表格 1 程序运行结果分析表
    矩阵乘法的MPI并行实验报告由表格1可知,随着进程数的增加,程序运行时间也随之减少,加速比随之增加。但是,可以注意到,单进程和2进程的时间花销相差并不大,2进程时的加速比仅为1.29,其原因是程序在多进程运行时,由于设计思路是主进程(0号进程)并不参与计算,只负责初始化矩阵和分发汇总结果,故2进程时程序优化并不明显。当增加到4进程及8进程时,程序运行时间大大减少。其加速比曲线如图9。
    矩阵乘法的MPI并行实验报告
    由于实验要求的矩阵为2048*2048的双精度浮点方阵,故在代码完成后,先将矩阵维度设置为7,以检验程序结果是否准确,同时对于生成的随机数不设置种子,以保证每次程序运行的数据一致,从而确保实验数据准确,检验结果如图10所示;
    矩阵乘法的MPI并行实验报告图 10 检验矩阵数据

四、实验总结:

经过此次实验,文章来源地址https://www.toymoban.com/news/detail-479397.html

  1. 熟悉掌握了MPI几个基本函数,知晓Send/Recv通信接口的使用;
  2. 进一步了解MPI通信的相关原理;
  3. 掌握基本的MPI编程能力;

五、 附录(代码):

#include <iostream>
#include <cstdlib>
#include <ctime>
#include <cmath>
#include <string>
#include "mpi.h"
#define NUM 2048	//矩阵大小
using namespace std;

//打印数组(测试使用)
void printfArray(double* A, int length) {
	for (int i = 0; i < length; i++) {
		for (int j = 0; j < length; j++) {
			cout << A[i * length + j] << " " ;
		}
		cout << endl;
	}
}
//扩展矩阵并且初始化,多余部分填充0
void matGene(double* A, int size, int actual_size) {
	// actual size: 使用的矩阵极可能大于原始大小
	for (int i = 0; i < actual_size; i++) {
		for (int j = 0; j < actual_size; j++) {
			if (i < size && j < size)
				//初始化矩阵,随机生成双精度浮点数[-1,1]
				A[i * actual_size + j] = -1.0 + 1.0 * rand() / RAND_MAX * 2; //A[i][j]
			else A[i * actual_size + j] = 0;	//扩展部分填充0
		}
	}
}

//计算矩阵乘法(进程数为1时)
void matMulti(double* A, double* B, double* C, int m, int n, int p) {
	for (int i = 0; i < m; i++) {
		for (int j = 0; j < p; j++) {
			C[i * p + j] = 0;
			for (int k = 0; k < n; k++)
				C[i * p + j] += A[i * n + k] * B[k * p + j];
		}
	}
}

//返回不大于根号(n)的最大因子
int factor(int n) {
	double sqr_root = sqrt(n);
	for (int i = sqr_root; i >= 1; i--) {
		if (n % i == 0) return i;
	}
}

int main(int argc, char* argv[]) {

	int n = NUM; // 数组大小
	double beginTime, endTime;  //用于记录时间
	//srand((unsigned int)time(NULL));	//如果多次测试,可以注释这一句

	// 初始化MPI 
	int my_rank = 0, comm_sz = 0, namelen = 0;
	char processor_name[MPI_MAX_PROCESSOR_NAME];
	MPI_Init(&argc, &argv);
	MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);	//获取进程号
	MPI_Comm_size(MPI_COMM_WORLD, &comm_sz);	//获取进程数
	MPI_Get_processor_name(processor_name, &namelen);	//获得处理器名
	MPI_Status status;	//状态

	if (comm_sz == 1) { // no parallel
		// Prepare data
		double* A = new double[n * n + 2];
		double* B = new double[n * n + 2];
		double* C = new double[n * n + 2];
		int saveN = n;
		matGene(A, saveN, n);
		matGene(B, saveN, n);

		// 计算时间
		beginTime = MPI_Wtime();	//开始时间
		matMulti(A, B, C, n, n, n);
		endTime = MPI_Wtime();	//结束时间
		cout << processor_name << ":" << "单进程时间开销:" << endTime - beginTime << "s" << endl;

		//删除矩阵
		delete[] A;
		delete[] B;
		delete[] C;
	}

	else { // 进程数大于1时,启用并行

		int saveN = n;

		// 主要过程负责收集结果,矩阵必须与散度相等:实际n大于输入
		//计算矩阵大小(需扩展至负责计算的进程数的倍数)即comm_sz-1的倍数
		if (n % (comm_sz - 1) != 0) {
			n -= n % (comm_sz - 1);
			n += (comm_sz - 1);
		}

		/*
			将可用于计算的进程数(comm_sz-1)分解为a*b
			然后将全体行划分为a个部分,全体列划分为b个部分,
			从而将整个矩阵划分为size相同的(comm_sz-1)个块。
			每个子进程负责计算最终结果的一块,只需要接收A对应范围的行和B对应范围的列,
			而不需要把整个矩阵传过去。
		*/
		int a = (comm_sz - 1) / factor(comm_sz - 1);
		int b = factor(comm_sz - 1);
		int each_row = n / a;
		int each_column = n / b;

		//0号进程负责初始化矩阵,分发和汇总结果
		if (my_rank == 0) {
			double* A = new double[n * n + 2];
			double* B = new double[n * n + 2];
			double* C = new double[n * n + 2];

			// Prepare data
			//cout << "n = " << n << endl;
			//将矩阵的维度扩展到comm_sz-1的倍数,多余的部分用0填充,保证正确性。
			matGene(A, saveN, n);
			matGene(B, saveN, n);
			//计算开始时间
			beginTime = MPI_Wtime();

			//Send:发送数据,矩阵A的行和矩阵B的列至各进程
			//发送 A[beginRow:endRow, :], B[:, beginColumn:endColumn]
			for (int i = 1; i < comm_sz; i++) {	//发送数据到各进程(0号进程除外)
				int beginRow = ((i - 1) / b) * each_row;
				int beginColumn = ((i - 1) % b) * each_column;
				// A: beginRow ~ endRow
				MPI_Send(&A[beginRow * n + 0], each_row * n, MPI_DOUBLE, i, i, MPI_COMM_WORLD);
				// B: n times, once a row
				for (int j = 0; j < n; j++) {
					MPI_Send(&B[j * n + beginColumn], each_column, MPI_DOUBLE, i, i * n + j + comm_sz + 2, MPI_COMM_WORLD);
				}
			}

			//接受结果 Recv: C[beginRow:endRow, beginColumn:endColumn]
			for (int i = 1; i < comm_sz; i++) {
				int beginRow = ((i - 1) / b) * each_row;
				int endRow = beginRow + each_row;
				int beginColumn = ((i - 1) % b) * each_column;
				for (int j = beginRow; j < endRow; j++) {
					MPI_Recv(&C[j * n + beginColumn], each_column, MPI_DOUBLE, i, each_row * i + (j - beginRow), MPI_COMM_WORLD, &status);
				}
			}

			endTime = MPI_Wtime();	//结束时间
			//打印时间花销
			cout << processor_name << ":" << comm_sz << "个进程时间开销:" << endTime - beginTime << "s" << endl;
			//删除矩阵
			delete[] A;
			delete[] B;
			delete[] C;

		}
		else {

			double* partA = new double[each_row * n + 2]; // A[beginRow:endRow, :]
			double* partB = new double[n * each_column + 2]; // B[:, beginColumn:endColumn]
			double* partC = new double[each_row * each_column + 2]; // C[beginRow:endRow, beginColumn:endColumn]

			//各进程接受数据  Recv: partA, partB
			MPI_Recv(&partA[0 * n + 0], each_row * n, MPI_DOUBLE, 0, my_rank, MPI_COMM_WORLD, &status);
			for (int j = 0; j < n; j++) {
				MPI_Recv(&partB[j * each_column + 0], each_column, MPI_DOUBLE, 0, my_rank * n + j + comm_sz + 2, MPI_COMM_WORLD, &status);
			}
			//计算
			matMulti(partA, partB, partC, each_row, n, each_column);

			//发送计算之后的结果	Send: partC

			for (int j = 0; j < each_row; j++) {
				MPI_Send(&partC[j * each_column + 0], each_column, MPI_DOUBLE, 0, each_row * my_rank + j, MPI_COMM_WORLD);
			}

			//删除数组
			delete[] partA;
			delete[] partB;
			delete[] partC;
		}

	}
	//终止MPI
	MPI_Finalize();
	return 0;
}

到了这里,关于矩阵乘法的MPI并行实验报告的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 高性能计算的矩阵乘法优化 - Python +MPI的实现

    本次实验的目的是使用MPI的并行性来进行矩阵乘法优化,本人使用 Python 实现 实验硬件: CPU :AMD Ryzen 7 5800H(3.20 GHz) 内存 :32GB (3200MHz) 要求 :使用一个矩阵,一个向量相乘,分别用单进程和多进程的mpi接口实现。 全局的规模参数是 Scale 数据示例 : 当 Scale=5 时,数据示例如

    2023年04月22日
    浏览(22)
  • CUDA 以及MPI并行矩阵乘连接服务器运算vscode配置

    CUDA 以及MPI并行矩阵乘连接服务器运算vscode配置

    本地安装 服务器端安装 c_cpp_properties.json launch.json tasks.json     本地安装和服务器端安装的扩展和CUDA一样 c_cpp_properties.json launch.json settings.json tasks.json

    2024年04月27日
    浏览(11)
  • 基于因特尔OneAPI实现矩阵并行乘法运算

    OneAPI介绍 Intel oneAPI 是一个跨行业、开放、基于标准的统一的编程模型,旨在提供一个适用于各类计算架构的统一编程模型和应用程序接口。其核心思想是使开发者只需编写一次代码,便可在跨平台的异构系统上运行,支持的底层硬件架构包括 CPU、GPU、FPGA、神经网络处理器以

    2024年02月04日
    浏览(6)
  • 网络应用编程 实验3 矩阵并行计算练习实验

    网络应用编程 实验3 矩阵并行计算练习实验

    编写一个 WPF 应用程序,利用 数据并行 计算两个矩阵(M×N和N×P)的乘积,得到一个M×P的矩阵。   具体要求 (1)在代码中用 多任务 通过 调用某方法 实现 矩阵并行 运算,在调用的参数中分别 传递M、N、P 的大小。 (2)程序中 至少要测试3次 有代表性的不同大小的矩阵运

    2024年02月05日
    浏览(10)
  • 使用mpi并行技术实现快排Qsort()

    使用mpi并行技术实现快排Qsort()

    快排基本原理: 快速排序可以说是最为常见的排序算法,冒泡排序时间复杂度达到了O(N2),而桶排序容易造成浪费空间。快排(Quicksort)就成为了不错的选择。 1、原理:快排需要找一个数作为基准数,用来参照。(可取第一个数为参照)         基准数在中间某位置,

    2024年02月10日
    浏览(13)
  • 【HNU分布式与云计算系统】MPI实现矩阵乘矩阵运算

    【HNU分布式与云计算系统】MPI实现矩阵乘矩阵运算

    实验环境 操作系统:Ubuntu 20.04 编程语言:C++ 实验原理 什么是MPI MPI是一个跨语言的通讯协议,用于编写并行计算机。支持点对点和广播。MPI是一个信息传递应用程序接口,包括协议和和语义说明,他们指明其如何在各种实现中发挥其特性。MPI的目标是高性能,大规模性,和

    2023年04月08日
    浏览(9)
  • 【矩阵乘法】C++实现外部矩阵乘法

    【矩阵乘法】C++实现外部矩阵乘法

    ​ 使用文件和内存模拟系统缓存,并利用矩阵乘法验证实际和理论情况。 设计一个 Matrix 类,其中 Matrix 是存在磁盘中的一个二进制文件,类通过保存的矩阵属性来读取磁盘。前八个字节为两个 int32 ,保存矩阵的行列数。 Matrix中有一个 buffer 成员为读取到的数据缓存,通过

    2024年02月11日
    浏览(8)
  • python矩阵乘法全面解读,python矩阵乘法常用代码

    python矩阵乘法全面解读,python矩阵乘法常用代码

      矩阵乘法,顾名思义是矩阵的乘法,矩阵相乘的含义是两个向量的积,在 Python中一般以乘号或括号表示。与常用的加、减、乘、除运算不同,矩阵乘法只能用于对给定矩阵进行乘法运算,不能进行除法运算。若要计算矩阵乘法的值,必须先进行矩阵分解。 在上一篇文章中

    2024年02月08日
    浏览(10)
  • 矩阵乘法(矩阵乘矩阵)

    矩阵乘法(矩阵乘矩阵)

    首先理了解矩阵是什么: 矩阵是一个按照长方阵列排列的复数或实数集合。(相信大家都懂) 关于矩阵的基本概念: 1.方阵:n 阶方阵 (正方形嘛) 2.同型矩阵:两个矩阵,行数与列数对应相同,称为同型矩阵 矩阵加减法: 在了解矩阵乘法前先看看矩阵加减法: 1.两个矩阵

    2024年02月08日
    浏览(11)
  • 项目调研丨多区块并行处理公链 Transformers 研究报告

    项目调研丨多区块并行处理公链 Transformers 研究报告

    目录 一、项目简介 二、项目愿景 三、特色和优势 (1)速度 (2)安全 (3)可扩展性 (4)高度定制 (5)不可篡改 (6)所有数据公开透明 (7)支持智能合约 四、发展历史 五、团队背景 六、融资信息 七、项目架构 (1)网络 (2)共识算法 (3)DAG (4)同步化 (5)交易

    2024年02月10日
    浏览(11)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包