mpi实现矩阵乘法,卷积,池化(gemm,covn,pooling)

news2024/11/17 4:45:49

矩阵乘法:

卷积:

池化:

Mpi基本原理:

  1.什么是MPI

Massage Passing Interface:是消息传递函数库的标准规范,由MPI论坛开发。

一种新的库描述,不是一种语言。共有上百个函数调用接口,提供与C和Fortran语言的绑定

MPI是一种标准或规范的代表,而不是特指某一个对它的具体实现

MPI是一种消息传递编程模型,并成为这种编程模型的代表和事实上的标准

2.MPI的特点

MPI有以下的特点:

消息传递式并行程序设计

指用户必须通过显式地发送和接收消息来实现处理机间的数据交换。

在这种并行编程中,每个并行进程均有自己独立的地址空间,相互之间访问不能直接进行,必须通过显式的消息传递来实现。

这种编程方式是大规模并行处理机(MPP)和机群(Cluster)采用的主要编程方式。

并行计算粒度大,特别适合于大规模可扩展并行算法

用户决定问题分解策略、进程间的数据交换策略,在挖掘潜在并行性方面更主动,并行计算粒度大,特别适合于大规模可扩展并行算法

消息传递是当前并行计算领域的一个非常重要的并行程序设计方式

二、MPI的基本函数

MPI调用借口的总数虽然庞大,但根据实际编写MPI的经验,常用的MPI函数是以下6个:

MPI_Init(…);

MPI_Comm_size(…);

MPI_Comm_rank(…);

MPI_Send(…);

MPI_Recv(…);

MPI_Finalize();

三、MPI的通信机制

MPI是一种基于消息传递的编程模型,不同进程间通过消息交换数据。

1.MPI点对点通信类型

所谓点对点的通信就是一个进程跟另一个进程的通信,而下面的聚合通信就是一个进程和多个进程的通信。

  1. 标准模式:

该模式下MPI有可能先缓冲该消息,也可能直接发送,可理解为直接送信或通过邮局送信。是最常用的发送方式。

由MPI决定是否缓冲消息

没有足够的系统缓冲区时或出于性能的考虑,MPI可能进行直接拷贝:仅当相应的接收完成后,发送语句才能返回。

这里的系统缓冲区是指由MPI系统管理的缓冲区。而非进程管理的缓冲区。

MPI环境定义有三种缓冲区:应用缓冲区、系统缓冲区、用户向系统注册的通信用缓冲区

MPI缓冲消息:发送语句在相应的接收语句完成前返回。

这时后发送的结束或称发送的完成== 消息已从发送方发出,而不是滞留在发送方的系统缓冲区中。

该模式发送操作的成功与否依赖于接收操作,我们称之为非本地的,即发送操作的成功与否跟本地没关系。

矩阵乘法代码实现:

#include<stdio.h>
#include<stdlib.h>
#include<mpi.h>
#include<time.h>

int main(int argc,char *argv[])
{
    double start, stop;
    int i, j, k, l;
    int *a, *b, *c, *buffer, *ans;
    int size;
    size = atoi(argv[1]);
    int rank, numprocs, line;
    

    MPI_Init(&argc, &argv);//MPI Initialize
    MPI_Comm_rank(MPI_COMM_WORLD,&rank);//获得当前进程号
    MPI_Comm_size(MPI_COMM_WORLD,&numprocs);//获得进程个数

    line = size/numprocs;//将数据分为(进程数)个块,主进程也要处理数据
    a = (int*)malloc(sizeof(int)*size*size);
    b = (int*)malloc(sizeof(int)*size*size);
    c = (int*)malloc(sizeof(int)*size*size);
    //缓存大小大于等于要处理的数据大小,大于时只需关注实际数据那部分
    buffer = (int*)malloc(sizeof(int)*size*line);//数据分组大小
    ans = (int*)malloc(sizeof(int)*size*line);//保存数据块计算的结果

    //主进程对矩阵赋初值,并将矩阵N广播到各进程,将矩阵M分组广播到各进程
    if (rank==0)
    {
        printf("tast %d start\n", rank);
        //从文件中读入矩阵
        FILE *fp;

        fp=fopen("a.txt","r");//打开文件
        start = MPI_Wtime();
        for(i=0;i<size;i++) //读数据
            for(j=0;j<size;j++)
                a[i*size+j] = i*size+j;    
        //将矩阵N发送给其他从进程
        for (i=1;i<numprocs;i++)
        {
                MPI_Send(b,size*size,MPI_INT,i,0,MPI_COMM_WORLD);
        }
        //依次将a的各行发送给各从进程
        for (l=1; l<numprocs; l++)
        {
            MPI_Send(a+(l-1)*line*size,size*line,MPI_INT,l,1,MPI_COMM_WORLD);
        }
        //接收从进程计算的结果
        for (k=1;k<numprocs;k++)
        {
            MPI_Recv(ans,line*size,MPI_INT,k,3,MPI_COMM_WORLD,MPI_STATUS_IGNORE);
            //将结果传递给数组c
            for (i=0;i<line;i++)
            {
                for (j=0;j<size;j++)
                {
                    c[((k-1)*line+i)*size+j] = ans[i*size+j];
                }

            }
        }
        //计算a剩下的数据
        for (i=(numprocs-1)*line;i<size;i++)
        {
            for (j=0;j<size;j++)
            {
                int temp=0;
                for (k=0;k<size;k++)
                    temp += a[i*size+k]*b[k*size+j];
                c[i*size+j] = temp;
            }
        }

        fp=fopen("c.txt","w");
        for(i=0; i<size; i++){
            for(j=0; j<size; j++)
                fprintf(fp,"%d ",c[i*size+j]);
            fputc('\n',fp);
        }
        fclose(fp);
        //结果测试
        //统计时间
        stop = MPI_Wtime();
        printf("tast %d end\n", rank);
        printf("rank:%d time:%lfs\n",rank,stop-start); 

        free(a);
        free(b);
        free(c);
        free(buffer);
        free(ans);
        
    }

    //其他进程接收数据,计算结果后,发送给主进程
    else
    {
        printf("tast %d start\n", rank);
        //接收广播的数据(矩阵b)
        MPI_Recv(b,size*size,MPI_INT,0,0,MPI_COMM_WORLD,MPI_STATUS_IGNORE);

        MPI_Recv(buffer,size*line,MPI_INT,0,1,MPI_COMM_WORLD,MPI_STATUS_IGNORE);
        //计算乘积结果,并将结果发送给主进程
        for (i=0;i<line;i++)
        {
            for (j=0;j<size;j++)
            {
                int temp=0;
                for(k=0;k<size;k++)
                    temp += buffer[i*size+k]*b[k*size+j];
                ans[i*size+j]=temp;
            }
        }
        //将计算结果传送给主进程
        MPI_Send(ans,line*size,MPI_INT,0,3,MPI_COMM_WORLD);
        printf("tast %d end\n", rank);
    }

    MPI_Finalize();//结束

    return 0;
}

运行结果:

矩阵相乘:

 卷积代码实现:

#include <mpi.h>
#include <iostream>
#include <vector>
#include<time.h>

using namespace std;

// 定义卷积函数
vector<vector<double>> convolution(vector<vector<double>> image, vector<vector<double>> kernel) {
    int image_height = image.size();
    int image_width = image[0].size();
    int kernel_height = kernel.size();
    int kernel_width = kernel[0].size();
    int output_height = image_height - kernel_height + 1;
    int output_width = image_width - kernel_width + 1;
    vector<vector<double>> output(output_height, vector<double>(output_width, 0.0));
    for (int i = 0; i < output_height; i++) {
        for (int j = 0; j < output_width; j++) {
            for (int k = 0; k < kernel_height; k++) {
                for (int l = 0; l < kernel_width; l++) {
                    output[i][j] += image[i + k][j + l] * kernel[k][l];
                }
            }
        }
    }
    return output;
}

int main(int argc, char** argv) {
    MPI_Init(&argc, &argv);
    int rank, size;
    double start, stop;
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    MPI_Comm_size(MPI_COMM_WORLD, &size);

    // 定义图像和卷积核
    vector<vector<double>> image = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};
    vector<vector<double>> kernel = {{1, 0}, {0, 1}};
    

    


    // 计算每个进程需要处理的行数
    int rows_per_process = image.size() / size;
    int remainder = image.size() % size;
    int start_row = rank * rows_per_process;
    int end_row = start_row + rows_per_process;
    if (rank == size - 1) {
        end_row += remainder;
    }

    // 每个进程处理自己的部分图像
    vector<vector<double>> local_image(end_row - start_row, vector<double>(image[0].size(), 0.0));
    for (int i = start_row; i < end_row; i++) {
        for (int j = 0; j < image[0].size(); j++) {
            local_image[i - start_row][j] = image[i][j];
        }
    }

    // 每个进程计算自己的卷积结果
    vector<vector<double>> local_output = convolution(local_image, kernel);

    // 将每个进程的卷积结果发送给主进程
    if (rank == 0) {
        printf("tast %d start\n", rank);
        start = MPI_Wtime();
        vector<vector<double>> output(image.size() - kernel.size() + 1, vector<double>(image[0].size() - kernel[0].size() + 1, 0.0));
        for (int i = 0; i < local_output.size(); i++) {
            for (int j = 0; j < local_output[0].size(); j++) {
                output[i][j] = local_output[i][j];
            }
        }
        for (int i = 1; i < size; i++) {
            int start_row = i * rows_per_process;
            int end_row = start_row + rows_per_process;
            if (i == size - 1) {
                end_row += remainder;
            }
            vector<vector<double>> temp_output(end_row - start_row, vector<double>(image[0].size() - kernel[0].size() + 1, 0.0));
            MPI_Recv(&temp_output[0][0], (end_row - start_row) * (image[0].size() - kernel[0].size() + 1), MPI_DOUBLE, i, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
            for (int j = start_row; j < end_row; j++) {
                for (int k = 0; k < image[0].size() - kernel[0].size() + 1; k++) {
                    output[j - kernel.size() + 1][k] = temp_output[j - start_row][k];
                }
            }
        }
        // 输出卷积结果
       // for (int i = 0; i < output.size(); i++) {
         //   for (int j = 0; j < output[0].size(); j++) {
           //     cout << output[i][j] << " ";
            //}
            //cout << endl;
        //}

        stop = MPI_Wtime();
        printf("tast %d end\n", rank);
        printf("rank:%d time:%lfs\n",rank,stop-start);

    } else {
        printf("tast %d start\n", rank);
        MPI_Send(&local_output[0][0], (end_row - start_row) * (image[0].size() - kernel[0].size() + 1), MPI_DOUBLE, 0, 0, MPI_COMM_WORLD);
        printf("tast %d end\n", rank);
    }

    MPI_Finalize();
    return 0;
}

运行结果:

卷积:

 池化代码实现:

#include <iostream>
#include <vector>
#include <mpi.h>
#include <climits>
using namespace std;

vector<vector<int>> max_pooling(vector<vector<int>> img, int kernel_size, int rank, int size) {
    int img_height = img.size();
    int img_width = img[0].size();
    int pool_height = img_height / kernel_size;
    int pool_width = img_width / kernel_size;
    int pool_size = pool_height * pool_width;
    int pool_per_process = pool_size / size;
    int remainder = pool_size % size;
    int start_index = rank * pool_per_process;
    int end_index = (rank + 1) * pool_per_process;
    if (rank == size - 1) {
        end_index += remainder;
    }
    vector<vector<int>> pool(end_index - start_index, vector<int>(1));
    int pool_index = 0;
    for (int i = 0; i < pool_height; i++) {
        for (int j = 0; j < pool_width; j++) {
            if (pool_index >= start_index && pool_index < end_index) {
                int max_val = INT_MIN;
                for (int k = 0; k < kernel_size; k++) {
                    for (int l = 0; l < kernel_size; l++) {
                        int val = img[i * kernel_size + k][j * kernel_size + l];
                        if (val > max_val) {
                            max_val = val;
                        }
                    }
                }
                pool[pool_index - start_index][0] = max_val;
            }
            pool_index++;
        }
    }
    return pool;
}

int main(int argc, char** argv) {
    int rank, size;
    double start, stop;
    MPI_Init(&argc, &argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    MPI_Comm_size(MPI_COMM_WORLD, &size);
    int img_height = 8;
    int img_width = 8;
    int kernel_size = 4;
    vector<vector<int>> img(img_height, vector<int>(img_width));
    for (int i = 0; i < img_height; i++) {
        for (int j = 0; j < img_width; j++) {
            //img[i][j] = i * img_width + j + 1;
        }
    }
    vector<vector<int>> pool = max_pooling(img, kernel_size, rank, size);
    vector<vector<int>> all_pool(4 * 4, vector<int>(1));
    MPI_Gather(&pool[0][0], pool.size() * pool[0].size(), MPI_INT, &all_pool[0][0], pool.size() * pool[0].size(), MPI_INT, 0, MPI_COMM_WORLD);
    if (rank == 0) {
        //printf("task:%d start\n",rank);
        start = MPI_Wtime();
        for (int i = 0; i < all_pool.size(); i++) {
           cout << "";
        }
        cout << endl;
       // printf("task:%d end\n",rank);
    }
    
    stop = MPI_Wtime();
    if (rank == 0){
    printf("rank:%d time:%lfs\n",rank,stop-start); 
    }
    MPI_Finalize();
    return 0;
}

池化:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/661585.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Rust日报】2023-06-16 Rust在Stack Overflow上连续8年成为最受欢迎语言(文末有彩蛋)...

推荐23个Rust Top开源项目 根据LibHunt网站Rust主题下有关统计&#xff0c;这里列出前5个Rust Top开源项目&#xff1a;deno、rust、tauri、alacritty、rustdesk。 阅读更多&#xff1a;Top 23 Rust Open-Source Projects https://www.libhunt.com/topic/rust Tweets&#xff1…

微信小程序触底加载scroll-view

微信小程序触底加载 scroll-view 了解什么是触底加载&#xff1f; 需求&#xff1a;有个固定高度的容器&#xff0c;实现容器里面的内容触底加载 1、内容盒子的高度 2、盒子里内容的总高度 3、滚动条的scrollTop 触底加载的原理就是 当里面的容器触底的时候进行分页&#xff0…

【题解】[ABC306G] Return to 1(数论)

【题解】[ABC306G] Return to 1 题目链接 ABC306G - Return to 1 题意概述 本题多测&#xff0c;\(T\) 组数据。 对于每组数据&#xff0c;给定一个 \(n\) 个点 \(m\) 条边的有向图&#xff0c;无重边自环。 问从顶点 \(1\) 出发&#xff0c;能否恰好走 \({10^{10}}^{100}\) 步…

【八大排序(七)】归并排序初级篇-递归版

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:八大排序专栏⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习排序知识   &#x1f51d;&#x1f51d; 归并排序 1. 前言2. 归并排序基本思路3. …

在数组中各位置上计算该位置以前所有元素的累加结果(Nan值视为0)numpy.nancumsum()

【小白从小学Python、C、Java】 【等级考试500强双证书考研】 【Python-数据分析】 在数组中各位置上计算该位置以前 所有元素的累加结果&#xff08;Nan值视为0&#xff09; numpy.nancumsum() [太阳]选择题 以下说法错误的是&#xff1a; import numpy as np a np.array([[n…

使用兮克 2.5G 交换机将北京联通 EPON 改为 ODI 猫棒接入

使用兮克 2.5G 交换机将北京联通 EPON 改为 ODI 猫棒接入 最近入手了兮克2.5G交换机&#xff08;兮克SKS1200-8GPY1XF&#xff09;&#xff0c;有 8 个 2.5G 电口和 1 个 10G SFP 光口&#xff1b;支持多种组网模式&#xff0c;其中一种是支持使用猫棒接入&#xff0c;由路由器…

聊聊微服务到底该如何划分

背景 现在动不动就是微服务架构&#xff0c;但是微服务划分的合理与否会极大的影响开发过程中的复杂度&#xff0c;划分的重要性不言而喻&#xff0c;但是在微服务划分这条路上并没有银弹&#xff0c;有的说DDD可以解决微服务的划分问题&#xff0c;吕哥想说的是那只是理论上的…

Linux常用命令——gcc命令

在线Linux命令查询工具 gcc 基于C/C的编译器 补充说明 gcc命令使用GNU推出的基于C/C的编译器&#xff0c;是开放源代码领域应用最广泛的编译器&#xff0c;具有功能强大&#xff0c;编译代码支持性能优化等特点。现在很多程序员都应用GCC&#xff0c;怎样才能更好的应用GCC…

Linux---上传和下载、压缩和解压

1. 上传下载 可以通过FinalShell工具&#xff0c;方便的和虚拟机进行数据交换。 在FinalShell软件的下方窗体中&#xff0c;提供了Linux的文件系统视图&#xff0c;可以方便的&#xff1a; 浏览文件系统&#xff0c;找到合适的文件&#xff0c;右键点击下载&#xff0c;即可…

STL中set与map介绍

目录 一. 键值对1. 关联式容器2. pair3. 应用场景 二. set1. set的介绍2. set的使用3. multiset的介绍 三. map1. map的介绍2. map的使用3. multimap的介绍 一. 键值对 ​ 用来表示具有一一对应关系的一种结构&#xff0c;该结构中一般只包含两个成员变量key和value。key代表键…

Draw.io 高阶用法

drawio是一款非常不错画流程图的软件&#xff0c;而且是免费的&#xff0c;但大部分图形以2D为主&#xff0c;有时候 却需要一些3D效果来增强方案 举个例子&#xff1a; 需要变成这样&#xff0c;看起来更3D 方法&#xff1a; 先拖入一个圆形&#xff0c;把限制比例去掉&a…

【C数据结构】队列_Queue

目录 队列_Queue 【1】队列的概念及结构 【2】节点队列的实现 【2.1】队列的各个接口 【2.2】队列的初始化 【2.3】队列栈的释放 【2.4】队尾入队列 【2.5】队头出队列 【2.6】获取队列头部元素 【2.7】获取队列尾部元素 【2.8】获取队列中有效元素个数 【2.9】检测…

图像 检测 - CenterNet: Objects as Points (arXiv 2019)

CenterNet: Objects as Points - 目标作为点&#xff08;arXiv 2019&#xff09; 摘要1. 引言2. 相关工作3. 准备工作4. 目标作为点4.1 3D 检测4.2 人体姿态估计 5. 实施细节6. 实验6.1 目标检测6.1.1 附加实验 6.2 3D 检测6.3 姿态估计 7. 结论References附录A&#xff1a;模型…

Nginx安装Windows、Linux | 正向代理、反向代理、负载均衡

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; Nginx Nginx是一个高性能的HTTP和反向代理服务器&#xff0c;也可用作电子邮件代理服务器和通用TCP/UDP代理服务器。它是一个轻量级的Web服务器&#xff0c;可以作为静…

[进阶]Java:线程概述、线程创建方式

什么是线程&#xff1f; 线程(thread)是一个程序内部的一条执行路径。我们之前启动程序执行后&#xff0c;main方法的执行其实就是一条单独的执行路径。程序中如果只有一条执行路径&#xff0c;那么这个程序就是单线程的程序。 多线程是什么&#xff1f; 多线程是指从软硬件上…

【现代数据架构】面向初创公司的现代数据堆栈

“为工作使用正确的工具&#xff01;” 这句话一开始听起来很简单&#xff0c;但在实际方面实施起来却非常复杂。早期的初创公司发现很难选择生态系统中可用的各种工具&#xff0c;因为它们的数据将如何演变是非常不可预测的。 需要现代数据堆栈 在过去 10 年中&#xff0c;软件…

c++11 标准模板(STL)(std::basic_ios)(四)

定义于头文件 <ios> template< class CharT, class Traits std::char_traits<CharT> > class basic_ios : public std::ios_base 类 std::basic_ios 提供设施&#xff0c;以对拥有 std::basic_streambuf 接口的对象赋予接口。数个 std::basic_ios…

VMware Integrated OpenStack 7.3 - 支持 vSphere 8.0U1 和 NSX 4.1 并向下兼容

VMware Integrated OpenStack 7.3 - 支持 vSphere 8.0U1 和 NSX 4.1 并向下兼容 VMware 支持的 OpenStack 发行版&#xff1a;在 VMware 虚拟化技术之上运行企业级 OpenStack 云 请访问原文链接&#xff1a;https://sysin.org/blog/vmware-vio-7/&#xff0c;查看最新版。原创…

极易搭建的代码托管平台Gitea

这礼拜有点霉啊&#xff0c;先是日常自用的机器上&#xff0c;SSD 挂了&#xff0c;彻底识别不了的那种 隔了两天&#xff0c;用来写文章用的小机器上&#xff0c; 500G 的机械硬盘也挂了&#xff0c;重新格了一下&#xff0c;挂在玩客云上当个下载盘用吧 好在都有备份&#xf…

[进阶]Java:文件字节输出流、文件拷贝、资源释放

文件字节输出流&#xff08;FileOutputStream&#xff09;写数据出去的API 流的关闭和刷新 代码演示如下&#xff1a; ​​​​​​​ public class OutputStreamDemo04 {public static void main(String[] args) throws Exception {//1.创建一个文件字节输出流管道与目标文件…