【GPU】Nvidia CUDA 编程高级教程——利用蒙特卡罗法求解近似值(MPI方法)

news2025/7/5 11:59:53

博主未授权任何人或组织机构转载博主任何原创文章,感谢各位对原创的支持!
博主链接

本人就职于国际知名终端厂商,负责modem芯片研发。
在5G早期负责终端数据业务层、核心网相关的开发工作,目前牵头6G算力网络技术标准研究。


博客内容主要围绕:
       5G/6G协议讲解
       算力网络讲解(云计算,边缘计算,端计算)
       高级C语言讲解
       Rust语言讲解



利用蒙特卡罗法求解 𝜋 的近似值(MPI方法)

在这里插入图片描述

MPI

在单一程序中管理所有设备可能比较麻烦。这类工作通常看来就像我们目前所做的那样,对所有的可用设备进行循环,每次循环执行相同的操作(例如启动核函数)。使用 MPI(消息传递接口)可以大大简化程序。使用 MPI 时,我们独立多次启动相同的程序(单程序多数据范例)。在最常见的使用情况下,我们会在您的服务器中启动尽可能多的独立进程副本,并且每个副本只使用一个 GPU。
请添加图片描述

MPI Rank (成员编号)

每个独立进程都有一个唯一数字标识符与之相关联(称为它的 rank),并可提供信息,显示运行中的总进程数量。我们可以通过编程方式使用MPI_Comm_rank()来获取每个进程的rank,以及使用MPI_Comm_size()来获取进程的数量。有了这些信息,我们可以让每个成员做出独立的处理决策(同时仍然只使用源代码的一个副本)。例如,我们可以用cudaSetDevice()把 GPU(任意)设置为等于 MPI rank(假设成员的数量最多与 GPU 数量持平)。

有了 MPI,每个成员都可以直接独立地执行它的 N / number_of_gpus 次计算,然后对计算结果求和。这确实是使用 MPI 编写这类程序的最常见方法。

使用MPI

以下是在代码中使用 MPI 的一些详细信息。

初始化和结束MPI
必须在 MPI 程序的开头优先初始化 MPI,并在末尾结束使用MPI。

// Initialize MPI
MPI_Init(&argc, &argv);
...
// Finalize MPI
MPI_Finalize();

获取成员编号(rank)和成员数
得到成员编号(rank)和成员的总数。

int rank, num_ranks;

// MPI_COMM_WORLD 意味着我们想要包含所有进程。
// 在 MPI 中,可以创建只包含某些成员的“通信器”。
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &num_ranks);

将 GPU 与 MPI 成员关联
我们为每个成员选择一个 GPU。

// 每个成员(任意)选择与其成员号 rank 相对应的 GPU
int dev = rank;
cudaSetDevice(dev);

收集结果
在我们的程序中,我们将对所有成员的结果进行求和(归约),并将结果存储在某个成员中,该成员将执行最终计算并打印结果。按照惯例,这是 0 号成员 rank 0(通常称为“根”处理器)。

对于此类归约,我们将使用 MPI_Reduce,它指定需要归约的数据的位置(hits)、进行归约后的结果的位置(total_hits)、归约的项目数量(1)、归约的数据类型(MPI_INT)、归约要执行的操作(MPI_SUM)、存储结果的成员(root),以及哪些进程参与通信(MPI_COMM_WORLD)。

// 将所有成员的结果累加到第 0 号成员的结果中
int* total_hits;
total_hits = (int*) malloc(sizeof(int));

int root = 0;
MPI_Reduce(hits, total_hits, 1, MPI_INT, MPI_SUM, root, MPI_COMM_WORLD);

if (rank == root) {
    // 计算 pi 的最终值并打印结果
    ...
}

使用例子

#include <iostream>
#include <curand_kernel.h>

#include <mpi.h>

#define N 1024*1024

__global__ void calculate_pi(int* hits, int device) {
    int idx = threadIdx.x + blockIdx.x * blockDim.x;

    // 初始化随机数状态(网格中的每个线程不得重复)
    int seed = device;
    int offset = 0;
    curandState_t curand_state;
    curand_init(seed, idx, offset, &curand_state);

    // 在 (0.0, 1.0] 内生成随机坐标
    float x = curand_uniform(&curand_state);
    float y = curand_uniform(&curand_state);

    // 如果这一点在圈内,增加点击计数器
    if (x * x + y * y <= 1.0f) {
        atomicAdd(hits, 1);
    }
}


int main(int argc, char** argv) {
    // 初始化 MPI
    MPI_Init(&argc, &argv);

    // 获取我们的rank和rank总数
    // MPI_COMM_WORLD 意味着我们想要包含所有进程
    // (可以在 MPI 中创建仅
    // 包含某些rank的“通信器”)。

    int rank, num_ranks;
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    MPI_Comm_size(MPI_COMM_WORLD, &num_ranks);

    // 确保我们的rank个数不超过 GPU 数量

    int device_count;
    cudaGetDeviceCount(&device_count);

    if (num_ranks > device_count) {
        std::cout << "Error: more MPI ranks than GPUs" << std::endl;
        return -1;
    }

    // 每个rank(任意)选择与其rank对应的 GPU
    int dev = rank;
    cudaSetDevice(dev);

    // 分配主机和设备值
    int* hits;
    hits = (int*) malloc(sizeof(int));

    int* d_hits;
    cudaMalloc((void**) &d_hits, sizeof(int));

    // 初始化点击次数并复制到设备
    *hits = 0;
    cudaMemcpy(d_hits, hits, sizeof(int), cudaMemcpyHostToDevice);

    // 启动核函数进行计算
    int threads_per_block = 256;
    int blocks = (N / device_count + threads_per_block - 1) / threads_per_block;

    calculate_pi<<<blocks, threads_per_block>>>(d_hits, dev);
    cudaDeviceSynchronize();

    // 将最终结果复制回主机
    cudaMemcpy(hits, d_hits, sizeof(int), cudaMemcpyDeviceToHost);

    // 将所有rank的结果累加到 rank 0 的结果中
    int* total_hits;
    total_hits = (int*) malloc(sizeof(int));

    int root = 0;
    MPI_Reduce(hits, total_hits, 1, MPI_INT, MPI_SUM, root, MPI_COMM_WORLD);

    if (rank == root) {
        // 计算 pi 的最终值
        float pi_est = (float) *total_hits / (float) (N) * 4.0f;

        // 打印结果
        std::cout << "Estimated value of pi = " << pi_est << std::endl;
        std::cout << "Error = " << std::abs((M_PI - pi_est) / pi_est) << std::endl;
    }

    // 清理
    free(hits);
    cudaFree(d_hits);

    // 最终确定 MPI
    MPI_Finalize();

    return 0;
}



在这里插入图片描述

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

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

相关文章

无线蓝牙运动耳机推荐,目前实用性不错的运动耳机推荐

对于运动人士来说&#xff0c;运动耳机是日常出门标配&#xff0c;一款实用性好的运动耳机能给我们带来很大的动力&#xff0c;在运动时戴着听音乐&#xff0c;能够释放压力的同时让运动过程变得更加心情愉快。但是运动耳机实在是太多了&#xff0c;琳琅满目&#xff0c;各种各…

c++11特性(3)

1.lambda表达式 a.出现由来&#xff1a; 以sort为例&#xff0c;我们可以传入一个仿函数对自定义类型进行排序。但是&#xff0c;对每一种比较方式我们都要显示传一个仿函数太麻烦。要是乱命名更加要命&#xff0c;那就除了写这段代码的人没人看得懂了&#xff01; b.lambda…

基于象虫损害优化算法的投资组合问题(Matlab代码实现)

&#x1f4dd;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;…

我的写作心得

前言 首先&#xff0c;博主是一个在校高中生&#xff0c;平常可以说几乎没有时间来创作文章&#xff0c;所以说断更也是很正常的 时隔半年&#xff0c;我也在csdn累计了400多个粉丝&#xff08;截止我创作这篇文章的时候&#xff09;&#xff0c;从之前的一无所有&#xff0c…

PCL交互选择ROI区域

PCL的配置和如何配准点云可见博主之前的博客 win10环境下PCL安装和配置回顾&#xff08;一&#xff09;_竹叶青lvye的博客-CSDN博客_pcl win10 win10环境下PCL安装和配置回顾&#xff08;二&#xff09;_竹叶青lvye的博客-CSDN博客_win10 安装pcl PCL - 3D点云配准(registra…

状态保持-JWT

“ Web的状态保持技术 -JWT&#xff08;Json Web Token&#xff09;” 在分布式微服务技术流行的今天&#xff0c;大型网站对JWT的使用愈加频繁&#xff0c;相比较于传统的session cookie。 HTTP 是一个无状态的协议&#xff0c;何为无状态呢&#xff1f;就是说这本次请求和上次…

【毕业设计】67-基于单片机的三相电压型SPWM逆变器控制设计及应用(仿真、原理图、源代码、低重复参考文档、答辩PPT、英文文献)

【毕业设计】67-基于单片机的三相电压型SPWM逆变器控制设计及应用&#xff08;仿真、原理图、源代码、低重复参考文档、答辩PPT、英文文献&#xff09; 文章目录【毕业设计】67-基于单片机的三相电压型SPWM逆变器控制设计及应用&#xff08;仿真、原理图、源代码、低重复参考文…

网站文章采集器-万能文章采集器

随着时代的发展。互联网无时不刻地出现在我们的生活中&#xff0c;大家也越来越注重效率&#xff0c;今天小编就给大家来分享一款网站文章采集器。只需要点几下鼠标就能轻松获取数据&#xff0c;不管是导出本地还是发布到网上。网站人员可以实现自动采集&#xff0c;定时发布&a…

[计算机毕业设计]食品安全数据的关联分析模型的应用

前言 &#x1f4c5;大四是整个大学期间最忙碌的时光,一边要忙着准备考研,考公,考教资或者实习为毕业后面临的就业升学做准备,一边要为毕业设计耗费大量精力。近几年各个学校要求的毕设项目越来越难,有不少课题是研究生级别难度的,对本科同学来说是充满挑战。为帮助大家顺利通过…

idea里面完整创建maven项目(包含如何使用)

目录 前言&#xff1a; 1.Maven和Maven Archetype区别&#xff1f; 2.创建maven项目之前的步骤&#xff08;必看&#xff09; 一、创建maven 1.打开idea--->文件--->新建--->项目 2.新建项目--->自定义项目名称--->选择Maven-->创建 3. 创建成功 二、…

微信小程序连接蓝牙汉印HM-A300L标签打印机

需求&#xff1a; 参考文章&#xff1a; 微信小程序实现蓝牙打印 打印机CPCL编程参考手册&#xff08;CPCL 语言&#xff09; 蓝牙打印机CPCL编程手册~汉印HM-A300 无用小知识&#xff1a; A300系列&#xff1a;先将打印机关机然后装好纸&#xff0c;同时按住屏幕左右两边的按…

UDS诊断网络层ISO15765-2(CAN)

诊断协议那些事儿 本文为诊断协议那些事儿专栏文章&#xff0c;从一个 ECU 到另一个 ECU&#xff0c;或外部诊断设备和一个 ECU 之间的通信&#xff0c;不仅依赖前文介绍的诊断寻址方式&#xff0c;更需要关注数据的传输&#xff0c;依赖网络层、传输层协议去完成&#xff0c;…

[附源码]java毕业设计社团管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

2022.11.09第6次Javaweb上机——实现登录欢迎页面

前情回顾 Myeclipse的安装教程_科大云炬的博客-CSDN博客 【JavaWeb第1次上机练习】安装Tomcat并在本地浏览器成功运行第一个Hello world网站_科大云炬的博客-CSDN博客 【第2次JavaWeb上机练习】_科大云炬的博客-CSDN博客 第3次 JavaWeb上机练习&#xff1a;Servlet基础作业…

python工作任务流flow实时框架:prefect

python工作任务流flow实时框架&#xff1a;prefect prefect是一个python的工作任务流调度实时框架&#xff0c;prefect可以快速构建平台系统复杂模块间工作流的监测。当平台系统模块之间的调用链越来越复杂时候&#xff0c;任务执行起来&#xff0c;已经很难盘点清楚程序逻辑和…

删除链表中重复元素的问题

目录 一、删除所有重复元素&#xff0c;使每个元素只出现一次 1.1带头链表解法 1.2 递归方法 二、删除所有重复数字结点&#xff0c;只留下不同数字 2.1带头链表解法 2.2 递归方法 一、删除所有重复元素&#xff0c;使每个元素只出现一次 给定一个已排序的链表的头 hea…

Android App开发实战项目之模仿美图秀秀的抠图工具(附源码 简单易懂 可直接使用)

需要图片集和源码请点赞关注收藏后评论区留言~~~ 所谓抠图神器&#xff0c;就是从一副图片中扣出用户想要的某块区域 一、需求描述 美图的修图功能如此强大&#xff0c;离不开专业的图片加工技术&#xff0c;抠图便是其中重要的一项功能。在App界面底部点击抠图按钮&#xff…

mysql查询:行转列与列转行

目录 1. 行转列 1.1 什么是行转列 1.2 准备工作 1.3 行转列的实现原理 2. 列转行 2.1 什么是列转行 2.2 准备工作 2.3 列转行的实现原理 3. 总结 1. 行转列 1.1 什么是行转列 从表面理解&#xff0c;就是表里存储在行上的数据&#xff0c;在查询结果里展示在列上面。举…

基于stm32单片机随机数自动摇号抽奖系统

资料编号&#xff1a;099 下面是相关功能视频演示&#xff1a; 99-基于stm32单片机随机数自动摇号抽奖系统&#xff08;源码仿真全套资料&#xff09;采用stm32单片机作为主控&#xff0c;LCD1602显示&#xff0c;通过按键来重置生成随机数&#xff0c;类似于摇号和抽奖系统 …

如何实现广义的元交易(Meta Transaction)

在合约内启用元交易是一个强大的补充。要求用户持有ETH来支付Gas一直以来都是而且仍然是新用户进入的最大挑战之一。如果只是简单的点击,谁知道现在会有多少人在使用以太坊? 但有时,解决方案可以在你的合约中加入元交易能力。实现起来可能比你想象的要容易。 什么是元交易?…