从一个GPU到多个GPU

news2024/11/23 22:37:37

在多GPU运行应用程序时,需要正确设计GPU之间的通信,GPU间数据传输的效率取决于GPU是如何连接在一个节点上并跨集群的
在多GPU系统里有两种连接方式
多GPU通过单个节点连接到PCIe总线上
多GPU连接到集群中的网络交换机上

/*
* 本示例演示了如何使用 OpenMP API 为多个 GPU 编写应用程序
 在 CPU 端使用 OpenMP 进行线程处理的多 GPU 示例, 需要支持 OpenMP 2.0 的编译器
 */

#include <omp.h>
#include <stdio.h>  // 使用 stdio 函数,因为 C++ 流不一定是线程安全的
#include <helper_cuda.h>

using namespace std;

//一个简单的内核,只需将每个数组元素递增 b
__global__ void kernelAddConstant(int *g_a, const int b)
{
    int idx = blockIdx.x * blockDim.x + threadIdx.x;
    g_a[idx] += b;
}
// 一个谓词,用于检查每个数组元素是否被设置为其索引加上 b
int correctResult(int *data, const int n, const int b)
{
    for (int i = 0; i < n; i++)
        if (data[i] != i + b)
            return 0;

    return 1;
}
int main(int argc, char *argv[])
{
    int num_gpus = 0;   // CUDA GPU 数量

    printf("%s Starting...\n\n", argv[0]);

    /
    // 确定支持 CUDA 的 GPU 数量
    //
    cudaGetDeviceCount(&num_gpus);

    if (num_gpus < 1)
    {
        printf("no CUDA capable devices were detected\n");
        return 1;
    }
    /
    // 显示 CPU 和 GPU 配置
    //
    printf("number of host CPUs:\t%d\n", omp_get_num_procs());
    printf("number of CUDA devices:\t%d\n", num_gpus);

    for (int i = 0; i < num_gpus; i++)
    {
        cudaDeviceProp dprop;
        cudaGetDeviceProperties(&dprop, i);
        printf("   %d: %s\n", i, dprop.name);
    }

    printf("---------------------------\n");
    /
    // initialize data
    //
    unsigned int n = num_gpus * 8192;
    unsigned int nbytes = n * sizeof(int);
    int *a = 0;     // 指向 CPU 上数据的指针
    int b = 3;      // 数组递增的值
    a = (int *)malloc(nbytes);
    if (0 == a)
    {
        printf("couldn't allocate CPU memory\n");
        return 1;
    }
    for (unsigned int i = 0; i < n; i++)
        a[i] = i;
    
    // 运行与 CUDA 设备数量相同的 CPU 线程
    //每个 CPU 线程控制不同的设备,处理各自的数据部分。 
    // 使用的 CPU 线程数量有可能多于 CUDA 设备的数量,在这种情况下,多个 CPU 线程将在同一设备上分配资源并启动内核。
    // 例如,尝试 omp_set_num_threads(2 * num_gpus); 
    // 请注意,在 "omparallel "作用域内声明的所有变量都是 是每个 CPU 线程的局部变量
    //
    omp_set_num_threads(num_gpus);  //创建与 CUDA 设备数量相同的 CPU 线程
    //omp_set_num_threads(2*num_gpus);// 创建的 CPU 线程数量是 CUDA 设备数量的两倍
    #pragma omp parallel
    {
        unsigned int cpu_thread_id = omp_get_thread_num();
        unsigned int num_cpu_threads = omp_get_num_threads();

        // 设置并检查该 CPU 线程的 CUDA 设备
        int gpu_id = -1;
        checkCudaErrors(cudaSetDevice(cpu_thread_id % num_gpus));   // "% num_gpus "允许 CPU 线程数量多于 GPU 设备数量
        checkCudaErrors(cudaGetDevice(&gpu_id));
        printf("CPU thread %d (of %d) uses CUDA device %d\n", cpu_thread_id, num_cpu_threads, gpu_id);

        int *d_a = 0;   // 指向与该 CPU 线程相关联的设备上内存的指针
        int *sub_a = a + cpu_thread_id * n / num_cpu_threads;   // 指向该 CPU 线程数据部分的指针
        unsigned int nbytes_per_kernel = nbytes / num_cpu_threads;
        dim3 gpu_threads(128);  // 128 threads per block
        dim3 gpu_blocks(n / (gpu_threads.x * num_cpu_threads));

        checkCudaErrors(cudaMalloc((void **)&d_a, nbytes_per_kernel));
        checkCudaErrors(cudaMemset(d_a, 0, nbytes_per_kernel));
        checkCudaErrors(cudaMemcpy(d_a, sub_a, nbytes_per_kernel, cudaMemcpyHostToDevice));
        kernelAddConstant<<<gpu_blocks, gpu_threads>>>(d_a, b);

        checkCudaErrors(cudaMemcpy(sub_a, d_a, nbytes_per_kernel, cudaMemcpyDeviceToHost));
        checkCudaErrors(cudaFree(d_a));

    }
    printf("---------------------------\n");

    if (cudaSuccess != cudaGetLastError())
        printf("%s\n", cudaGetErrorString(cudaGetLastError()));


    
    // check the result
    //
    bool bResult = correctResult(a, n, b);

    if (a)
        free(a); // free CPU memory

    exit(bResult ? EXIT_SUCCESS : EXIT_FAILURE);
}

在这里插入图片描述

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

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

相关文章

【Vue-Router】路由模式

1. WebHashHistory index.ts import { createRouter, createWebHistory, RouteRecordRaw, createWebHashHistory } from "vue-router";// 路由模式 //vue2 mode history -> vue3 createWebHistory //vue2 mode hash -> vue3 createWebHashHistory //vue2 m…

电池的正极是带正电?

首先说明结论&#xff1a;电池正极带正电&#xff0c;负极带负电。 一个错误的实例&#xff1a; 如果说电流是从电池正极流动到电池负极&#xff0c;那么电子就是从负极流动到正极&#xff0c;那么正极就是带负电。----这个说法是错误的。这是因为&#xff0c;根据那么很出名…

verilog学习笔记5——进制和码制、原码/反码/补码

文章目录 前言一、进制转换1、十进制转二进制2、二进制转十进制3、二进制乘除法 二、原码、反码、补码1、由补码计算十进制数2、计算某个负数的补码 前言 2023.8.13 天气晴 一、进制转换 1、十进制转二进制 整数&#xff1a;除以2&#xff0c;余数倒着写 小数&#xff1a;乘…

难解的bug

android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord 【Android TimeCat】 解决 context.startforegroundservice() did not then call service.startforeground() | XiChens Blog http://www…

【Linux从入门到精通】文件I/O操作(C语言vs系统调用)

文章目录 一、C语言的文件IO相关函数操作 1、1 fopen与fclose 1、2 fwrite 1、3 fprintf与fscanf 1、4 fgets与fputs 二、系统调用相关接口 2、1 open与close 2、2 write和read 三、简易模拟实现cat指令 四、总结 &#x1f64b;‍♂️ 作者&#xff1a;Ggggggtm &#x1f64b;‍…

JAVA多线程和并发基础面试问答(翻译)

JAVA多线程和并发基础面试问答(翻译) java多线程面试问题 1. 进程和线程之间有什么不同&#xff1f; 一个进程是一个独立(self contained)的运行环境&#xff0c;它可以被看作一个程序或者一个应用。而线程是在进程中执行的一个任务。Java运行环境是一个包含了不同的类和程序…

Shell编程之条件测试、if语句、case语句

条件语句 一、条件测试1.1 测试命令1.1 文件测试1.2 整数比较1.3 字符串比较1.4 逻辑测试1.4.1 逻辑与 &&1.4.2 逻辑或 || 1.4.3 组合应用1.5 多个命令组合执行 ( ) { } 二、if语句2.1单分支结构2.2 多分支结构2.4 if语句练习2.4.1 单分支2.4.2 简单的交互式分数反馈 三…

Shell编程之正则表达式(非常详细)

正则表达式 1.通配符和正则表达式的区别2.基本正则表达式2.1 元字符 &#xff08;字符匹配)2.2 表示匹配次数2.4 位置锚定2.5 分组 和 或者 3.扩展正则表达式4.部分文本处理工具4.1 tr 命令4.2 cut命令4.3 sort命令4.4 uniq命令 1.通配符和正则表达式的区别 通配符一般用于文件…

部署Springboot项目注意事项

步骤 步骤 1&#xff1a;将数据库内容在云服务器上的数据库部署一份 我使用mariadb&#xff1b;会出现一些不兼容现象&#xff1b;我们需要把默认值删掉 2&#xff1a;配置文件你得修改地方 a&#xff1a;linux是磁盘区分(像我自己项目用来储存验证码的文件我们得换这个配置;…

DoIP诊断入门

简介 DoIP&#xff08;Diagnosis over Internet Protocol&#xff09;是一种用于车辆诊断的网络通信协议。它基于现代互联网技术&#xff0c;允许通过以太网或IP网络进行车辆诊断和通信。 DoIP的背景是现代车辆中使用的电子控制单元&#xff08;ECU&#xff09;数量不断增加&…

利用OpenSSL实现私有 CA 搭建和证书颁发

利用OpenSSL实现私有 CA 搭建和证书颁发 一、私有 CA 搭建1. 安装openssl2. 配置 openssl3. 生成 CA 自己的私钥4. 生成 CA 自己的自签证书5. 验证自签证书 二、向私有CA申请证书流程1. 生成应用私钥文件2. 根据应用私钥生成证书申请文件3. 向CA请求颁发证书4. 验证应用证书5. …

PS/LR2024专用智能磨皮插件Portraiture提高P图效率

Portraiture 4智能磨皮插件支持Photoshop和Lightroom&#xff01;Portraiture是一款智能磨皮插件&#xff0c;为Photoshop和Lightroom添加一键磨皮美化功能&#xff0c;快速对照片中皮肤、头发、眉毛等部位进行美化&#xff0c;无需手动调整&#xff0c;大大提高P图效率。全新4…

Wlan——无线服务集和AP的基本概念以及AP的配置

目录 WLAN服务集的基本概念 AP的基本概念 AP的分类 AP模式的切换 胖&#xff08;FAT&#xff09;AP介绍 胖AP的工作模式 接入模式和路由模式的区别 胖AP的组网方式 瘦&#xff08;FIT&#xff09;AP介绍 瘦AP的工作模式 瘦AP的组网方式 胖AP和瘦AP的区别 AP的配置…

(leecode)错误的集合

最近听到的&#xff0c;还可以&#xff0c;试试吧~ 题目&#xff1a; 示例&#xff1a; 提示&#xff1a; 题解&#xff1a; 思路&#xff1a; 将数字大小的位置&#xff0c;然后遍历每个位置&#xff0c;大小为0的是缺失数字&#xff0c;大小为2的是重复数字 int* findErro…

2022年12月 C/C++(一级)真题解析#中国电子学会#全国青少年软件编程等级考试

第1题&#xff1a;加一 输入一个整数x&#xff0c;输出这个整数加1后的值&#xff0c;即x1的值。 时间限制&#xff1a;1000 内存限制&#xff1a;65536 输入 一个整数x(0 ≤ x ≤ 1000)。 输出 按题目要求输出一个整数。 样例输入 9 样例输出 10 以下是使用C语言编写的解决方案…

湘大 XTU OJ:1406 String Game、1098 素数个数 题解(非常详细)

1406 String Game 一、链接 1406 String Game 二、题目 题目描述 Alice和Bob正在玩一个基于字符串的游戏&#xff0c;一开始&#xff0c;Alice和Bob分别拥有一个等长的字符串S1和S2&#xff0c;且这两个字符串只包含小写字母。 在每个回合中&#xff0c;Alice和Bob必须分…

【Vue-Router】路由入门

路由&#xff08;Routing&#xff09;是指确定网站或应用程序中特定页面的方式。在Web开发中&#xff0c;路由用于根据URL的不同部分来确定应用程序中应该显示哪个内容。 构建前端项目 npm init vuelatest //或者 npm init vitelatest安装依赖和路由 npm install npm instal…

Python Opencv实践 - 图像旋转

import cv2 as cv import numpy as np import matplotlib.pyplot as pltimg cv.imread("../SampleImages/pomeranian.png", cv.IMREAD_COLOR)#图像旋转 #Opencv中的旋转&#xff0c;首先通过cv.getRotationMatrix2D获得旋转矩阵 #cv.getRotationMatrix2D(center,ang…

抖音小程序实现less语言编译样式

1.在抖音开发工具中搜索扩展less 2. 然后点击小齿轮选择扩展设置 3. 然后在扩展设置中选择在settings.json中编辑# 4. 在settings.json中加入以下这段代码即可 // Easy LESS配置"less.compile": {"compress": false,//是否压缩"sourceMap": fal…

腾讯云轻量应用服务器Typecho应用模板搭建博客流程

腾讯云百科分享使用腾讯云轻量应用服务器Typecho应用模板搭建博客流程&#xff0c;Typecho 是开源的博客建站平台&#xff0c;具有轻量、高效、稳定等特点&#xff0c;操作界面简洁友好。该镜像基于 CentOS 7.6 64 位操作系统&#xff0c;并已预置 Nginx、PHP、MariaDB 软件。您…