基于MPI的并行计算

代码实现的是基于MPI的并行计算,代码如下:

#include <stdio.h>                // needed for printing
#include <math.h>                // needed for tanh, used in init function
#include "params.h"                // model & simulation parameters
#include <mpi.h>

static int workerRank;
static int numWorkers;
static int rowsRange[501];
static MPI_Status status;
static int rangeBegin;
static int rangeEnd;

void init(double u[N][N], double v[N][N]){
    double uhi, ulo, vhi, vlo;
    uhi = 0.5; ulo = -0.5; vhi = 0.1; vlo = -0.1;
    for (int i=rangeBegin; i < rangeEnd; i++){
        for (int j=0; j < N; j++){
            u[i][j] = ulo + (uhi-ulo)*0.5*(1.0 + tanh((i-N/2)/16.0));
            v[i][j] = vlo + (vhi-vlo)*0.5*(1.0 + tanh((j-N/2)/16.0));
        }
    }
}

void dxdt(double du[N][N], double dv[N][N], double u[N][N], double v[N][N]){
    double lapu, lapv;
    int up, down, left, right;
    for (int i = rangeBegin; i < rangeEnd; i++){
        for (int j = 0; j < N; j++){
            if (i == 0){
                down = i;
            }
            else{
                down = i-1;
            }
            if (i == N-1){
                up = i;
            }
            else{
                up = i+1;
            }
            if (j == 0){
                left = j;
            }
            else{
                left = j-1;
            }
            if (j == N-1){
                right = j;
            }
            else{
                right = j+1;
            }
            lapu = u[up][j] + u[down][j] + u[i][left] + u[i][right] + -4.0*u[i][j];
            lapv = v[up][j] + v[down][j] + v[i][left] + v[i][right] + -4.0*v[i][j];
            du[i][j] = DD*lapu + u[i][j]*(1.0 - u[i][j])*(u[i][j]-b) - v[i][j];
            dv[i][j] = d*DD*lapv + c*(a*u[i][j] - v[i][j]);
        }
    }
}

void step(double du[N][N], double dv[N][N], double u[N][N], double v[N][N]){
    for (int i = rangeBegin; i < rangeEnd; i++){
        for (int j = 0; j < N; j++){
            u[i][j] += dt*du[i][j];
            v[i][j] += dt*dv[i][j];
        }
    }
}

double norm(double x[N][N]){
    double nrmx = 0.0;
    for (int i = rangeBegin; i < rangeEnd; i++){
        for (int j = 0; j < N; j++){
            nrmx += x[i][j]*x[i][j];
        }
    }
    /*
    集合通信函数
    规约函数 MPI_Reduce(),将通信子内各进程的同一个变量参与规约计算,并向指定的进程输出计算结果
    int MPI_Reduce(
        void *input_data, //指向发送消息的内存块的指针
        void *output_data, //指向接收(输出)消息的内存块的指针
        int count,//数据量
        MPI_Datatype datatype,//数据类型
        MPI_Op operator,//规约操作
        int dest,//要接收(输出)消息的进程的进程号
        MPI_Comm comm);//通信器,指定通信范围
        */
    double result;
    MPI_Reduce(&nrmx, &result, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
    return result;
}

// Calculate the range of rows for different processes
void calcRowsRange(){
    int numIndividual = N / numWorkers;
    int moreRows = N % numWorkers;
    for(int idx = 0; idx < numWorkers; ++idx){
        if(idx < moreRows){
             rowsRange[idx + 1] = rowsRange[idx] + numIndividual + 1;
        } else {
            rowsRange[idx + 1] = rowsRange[idx] + numIndividual;
        }
    }
    // Set the range of current worker
    rangeBegin = rowsRange[workerRank];
    rangeEnd = rowsRange[workerRank + 1];
}

// Swap local rows between adjacent worker
void swapRows(double u[N][N], double v[N][N]){
    // Swap local rows with next worker
    int nextWorkerRank = workerRank + 1;
    int prevWorkerRank = workerRank - 1;
    if(workerRank < numWorkers - 1){
        double *addr = &u[rowsRange[nextWorkerRank]][0];
        int n = (rowsRange[nextWorkerRank + 1] - rowsRange[nextWorkerRank]) * N;
        /*
        接收操作,在收到匹配消息之前不会返回
        addr:指向包含要发送的数据的缓冲区的指针
        n:缓冲区中元素的数目。 如果消息的数据部分为空,请将 count 参数设置为 0
        MPI_DOUBLE:缓冲区数组中元素的数据类型
        nextWorkerRank:指定通信器内发送进程的排名。 指定 MPI_ANY_SOURCE 常量,以指定任何源都是可接受的
        标签(0):用于区分不同类型的消息的消息标记。 指定 MPI_ANY_TAG 常量以指示任何标记都是可接受的
        MPI_COMM_WORLD:通信器的句柄
        status:返回时,包含指向 MPI_Status 结构的指针,其中存储了有关接收的消息的信息
        */
        MPI_Recv(addr, n, MPI_DOUBLE, nextWorkerRank, 0, MPI_COMM_WORLD, &status);

        addr = &u[rowsRange[workerRank]][0];
        n = (rowsRange[workerRank + 1] - rowsRange[workerRank]) * N;
        /*
        执行标准模式发送操作,并在可以安全重用发送缓冲区时返回 
        addr:指向包含要发送的数据的缓冲区的指针
        n:缓冲区中元素的数目。 如果消息的数据部分为空,请将 count 参数设置为 0
        MPI_DOUBLE:缓冲区数组中元素的数据类型
        nextWorkerRank:指定通信器内发送进程的排名
        标签(0):用于区分不同类型的消息的消息标记
        MPI_COMM_WORLD:通信器的句柄
        */
        MPI_Send(addr, n, MPI_DOUBLE, nextWorkerRank, 0, MPI_COMM_WORLD);
    }

    // Swap local rows with previous worker
    if(workerRank > 0){
        double *addr = &u[rowsRange[workerRank]][0];
        int n = (rowsRange[workerRank + 1] - rowsRange[workerRank]) * N;
        MPI_Send(addr, n, MPI_DOUBLE, prevWorkerRank, 0, MPI_COMM_WORLD);

        addr = u[rowsRange[prevWorkerRank]];
        n = (rowsRange[prevWorkerRank + 1] - rowsRange[prevWorkerRank]) * N;
        MPI_Recv(addr, n, MPI_DOUBLE, prevWorkerRank, 0, MPI_COMM_WORLD, &status);
    }
}

int main(int argc, char** argv){
    
    double t = 0.0, nrmu, nrmv;
    double u[N][N], v[N][N], du[N][N], dv[N][N];

    // Initialize the MPI program
    /*
    每一个被MPI程序调用的第一个MPI函数都是MPI_Init,
    要在调用任何MPI函数之前调用
    */
    MPI_Init(&argc, &argv);
    /*
    当MPI初始化后,每一个活动进程变成了一个叫MPI_COMM_WORLD的通信域中的成员。
    一个通信域是一个不透明的对象,提供了在进程之间传递消息的环境。
    在一个通信域内的进程是有序的,一个进程的序号便是它在整个排序中的位置。
    */
    MPI_Comm_rank(MPI_COMM_WORLD, &workerRank);
    /*
    进程可以通过调用函数MPI_Comm_size来确定一个通信域中的进程总数
    */
    MPI_Comm_size(MPI_COMM_WORLD, &numWorkers);

    // Calculate the range of rows for different processes
    calcRowsRange();
    
    FILE *fptr = fopen("part2.dat", "w");
    fprintf(fptr, "#t\t\tnrmu\t\tnrmv\n");
    
    // initialize the state
    init(u, v);
    // Swap local rows between adjacent worker
    swapRows(u, v);
    
    // time-loop
    for (int k=0; k < M; k++){
        // track the time
        t = dt*k;
        // evaluate the PDE
        dxdt(du, dv, u, v);
        // update the state variables u,v
        step(du, dv, u, v);
        // Swap local rows between adjacent worker
        swapRows(u, v);

        if (k%m == 0){
            // calculate the norms
            nrmu = norm(u);
            nrmv = norm(v);
            if(workerRank == 0){
                printf("t = %2.1f\tu-norm = %2.5f\tv-norm = %2.5f\n", t, nrmu, nrmv);
                fprintf(fptr, "%f\t%f\t%f\n", t, nrmu, nrmv);
            }
        }
    }
    
    if(workerRank == 0){
        fclose(fptr);
    }
    /*
    并行结束
    */
    MPI_Finalize();
    return 0;
}

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

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

相关文章

QT-串口工具

一、演示效果 二、关键程序 &#xff1a; #include "mainwindow.h" #include "ui_mainwindow.h"#include <QMessageBox>MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow),listPlugins(QList<TabPluginInt…

动态规划--持续更新篇

将数字变成0的操作次数 1.题目 2.思路 在numberOfSteps函数中&#xff0c;首先设置f[0]为0&#xff0c;因为0已经是0了&#xff0c;不需要任何步骤。然后&#xff0c;使用一个for循环从1迭代到输入的整数num。对于每个整数i&#xff0c;如果i是奇数&#xff0c;则将f[i]设置为…

静态时序分析:SDC约束命令set_driving_cell详解

相关阅读 静态时序分析https://blog.csdn.net/weixin_45791458/category_12567571.html?spm1001.2014.3001.5482 在上文中&#xff0c;我们不建议使用set_drive命令而是使用set_driving_cell命令&#xff0c;这是一个描述输入端口驱动能力更精确的方法。因为大多数情况下&…

SpringBoot实现缓存预热的几种常用方案

&#x1f3f7;️个人主页&#xff1a;牵着猫散步的鼠鼠 &#x1f3f7;️系列专栏&#xff1a;Java全栈-专栏 &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&…

如何使用1688.item_search_shop API获取阿里巴巴店铺商品信息

要使用1688的item_search_shop API获取阿里巴巴店铺的商品信息&#xff0c;你通常需要遵循以下步骤&#xff1a; 1. 注册并获取API密钥 首先&#xff0c;你需要在阿里巴巴开放平台&#xff08;如1688开放平台&#xff09;上注册一个开发者账号&#xff0c;并创建一个应用。创…

QEMU开发入门

1. 简介 QEMU&#xff08;Quick EMUlator&#xff09;是一个开源的虚拟化软件&#xff0c;它能够模拟多种硬件平台&#xff0c;并在这些平台上运行各种操作系统。QEMU可以在不同的主机架构之间进行虚拟化&#xff0c;例如x86、ARM、PowerPC、Risc-V等。QEMU是一个功能强大且灵…

大数据面试总结三

1、hdfs作为分布式存储系统&#xff0c;底层的实现的方式&#xff08;可能不正确&#xff09; 1、底层是一个分布式存储的&#xff0c;底层会将数据进行切分多个block块&#xff08;128M&#xff09;&#xff0c;并存储在不同的节点上面&#xff0c;这种分布式方式有助于提高数…

LabVIEW开发FPGA的高速并行视觉检测系统

LabVIEW开发FPGA的高速并行视觉检测系统 随着智能制造的发展&#xff0c;视觉检测在生产线中扮演着越来越重要的角色&#xff0c;尤其是在质量控制方面。传统的基于PLC的视觉检测系统受限于处理速度和准确性&#xff0c;难以满足当前生产需求的高速和高精度要求。为此&#xf…

前端页面生成条形码,借助layui打印标签

借助JsBarcode生成条形码 官网&#xff1a;https://lindell.me/JsBarcode/ github: https://github.com/lindell/JsBarcode <div class"table-div" style"display: block;width: 300px; height: 241px; margin: auto;"><table border"1&quo…

ACL权限、特殊位与隐藏属性的奥秘

1.2 操作步骤 # 1. 添加测试目录&#xff0c;用户&#xff0c;组&#xff0c;并将用户添加到组 ------------------- [rootlocalhost ~]# mkdir /project[rootlocalhost ~]# useradd zs[rootlocalhost ~]# useradd ls[rootlocalhost ~]# groupadd tgroup[rootlocalhost ~]# g…

软件提示找不到MSVCP140.dll是什么意思,修复MSVCP140.dll丢失的多个方法

msvcp140.dll 文件是 Microsoft Visual C 运行时库的一部分&#xff0c;具体来说它是 Visual Studio 2015 版本编译的C应用程序所依赖的一个动态链接库&#xff08;DLL&#xff09;文件。这个 DLL 文件包含了大量由Microsoft开发的标准C库函数&#xff0c;这些函数对于许多在Wi…

大模型综述总结--第一部分

1 目录 本文是学习https://github.com/le-wei/LLMSurvey/blob/main/assets/LLM_Survey_Chinese.pdf的总结&#xff0c;仅供学习&#xff0c;侵权联系就删 目录如下图 本次只总结一部分&#xff0c;刚学习有错请指出&#xff0c;VX关注晓理紫&#xff0c;关注后续。 2、概述…

Linux-进程相关函数接口-008

1【fork】 1.1函数原型 1.2函数功能 创建一个子进程&#xff0c;新创建的进程成为原来进程的子进程&#xff0c;原来的进程称为新进程的父进程。 1.3函数参数 1.3.1【】 1.4返回值 【成功】&#xff1a; 【失败】&#xff1a; 2【fork】 2.1函数原型 2.2函数功能 创…

红日靶场3

靶场链接&#xff1a;漏洞详情 在虚拟机的网络编辑器中添加两个仅主机网卡 信息搜集 端口扫描 外网机处于网端192.168.1.0/24中&#xff0c;扫描外网IP端口&#xff0c;开放了80 22 3306端口 80端口http服务&#xff0c;可以尝试登录网页 3306端口mysql服务&#xff0c;可…

linux卸载mysql8重装5

目录 背景操作卸载重装配置启动 背景 在linux&#xff08;阿里云ECS&#xff09;安装部署Hive时初始化Hive元数据库&#xff0c;遇到报错前一天两三小时没解决&#xff0c;问题定位为mysql&#xff0c;次日打算重装 操作 卸载 停止 MySQL 服务 systemctl stop mysql yum卸载…

ES6内置对象 - Map

Map&#xff08;Map对象保存键值对&#xff0c;键值均不限制类型&#xff09; 特点&#xff1a; 有序&#xff08;Set集合是无序的&#xff09;&#xff1b;键值对&#xff08;键可以是任意类型&#xff09;&#xff1b;键名不能重复&#xff08;如果重复&#xff0c;则覆盖&…

DK 树

推荐在 cnblogs 上阅读 引入 这是由 DengDuck 总结整理的一种处理线段树类问题的算法。 板题引入 给定数列 A { a i } A\{a_i\} A{ai​} 和 B { b i } B\{b_i\} B{bi​}。 其中有以下操作&#xff1a; C l r z&#xff1a; a i ← a i z a_i\leftarrow a_iz ai​←ai​…

ES相关问题

在Elasticsearch&#xff08;ES&#xff09;集群中&#xff0c;节点根据其配置和角色可以分为以下几种主要类型&#xff1a; Master Node&#xff08;主节点&#xff09;&#xff1a; 主节点负责管理整个集群的元数据&#xff0c;如索引的创建、删除、分片分配等。它维护着集群…

c编译器学习07:minilisp编译器改造(debug模式支持调试)

问题 原版的minilisp编译器不支持argv输入测试&#xff0c;不方便单步调试。 代码改造目标是既不改变原有程序的各种功能&#xff0c; 又能支持个人习惯的vs单步debug模式。 CMakeLists.txt变更 定义DEBUG宏 解决单步调试源码定位偏差问题 cmake_minimum_required(VERSION …

高级语言期末2012级B卷

1.编写函数&#xff0c;输出任意正整数n的位数&#xff08;n默认为存储十进制的整形变量&#xff09; 例如&#xff1a;正整数13&#xff0c;则输出2,&#xff1b;正整数3088&#xff0c;则输出4 #include <stdio.h>int func(int n) {int count0;while(n>0) {n/10;co…