矩阵乘法的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日
    浏览(85)
  • 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日
    浏览(30)
  • 基于因特尔OneAPI实现矩阵并行乘法运算

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    2024年02月10日
    浏览(29)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包