cuda lib 线程安全的要义

1, 概述

cuda lib 线程安全的几个多线程的情景:

单卡多线程;

多卡多线程-每卡单线程;

多卡多线程-每卡多线程;

需要考虑的问题:


每个 cublasHandle_t 只能有一个stream么?
每个cusolverHandle_t 只能有一个 stream么?
每个cusolverHandle_t只能有要给cublasHandle_t么?
多个线程同时跑在多个或一个gpu上,需要使用多个stream么?都是NULL 默认stream的话,不会影响效率吧?

2, 多线程示例

从如下代码思考开去,这个多线程场景是使用了openmp自动划分任务,也可以使用pthread来七分任务,这里对这个示例进行了注释:

https://github.com/NVIDIA/cuda-samples/blob/v11.4/Samples/cudaOpenMP/cudaOpenMP.cu




/*
 * Multi-GPU sample using OpenMP for threading on the CPU side
 * needs a compiler that supports OpenMP 2.0
 */
//使用openmp时包含其头文件
#include <omp.h>
#include <stdio.h>  // stdio functions are used since C++ streams aren't necessarily thread safe
//#include <helper_cuda.h>

#define checkCudaErrors(val)  val

using namespace std;

// a simple kernel that simply increments each array element by b
__global__ void kernelAddConstant(int *g_a, const int b)
{
    int idx = blockIdx.x * blockDim.x + threadIdx.x;
    g_a[idx] += b;
}

// a predicate that checks whether each array element is set to its index plus 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;   // number of CUDA GPUs

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

    /
    // determine the number of CUDA capable GPUs
    //
    cudaGetDeviceCount(&num_gpus);

    if (num_gpus < 1)
    {
        printf("no CUDA capable devices were detected\n");
        return 1;
    }

    /
    // display CPU and GPU configuration
    //
    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);// nbytes = num_gpus * [8192*sizeof(int)]
    printf("nbytes=%u\n", nbytes);
    int *a = 0;     // pointer to data on the CPU
    int b = 3;      // value by which the array is incremented
    a = (int *)malloc(nbytes);

    if (0 == a)
    {
        printf("couldn't allocate CPU memory\n");
        return 1;
    }
//这里如果使用 #pragma omp parallel for 的话,会不会影响下面的线程数?
    for (unsigned int i = 0; i < n; i++)
        a[i] = i;


    
    // run as many CPU threads as there are CUDA devices
    //   each CPU thread controls a different device, processing its
    //   portion of the data.  It's possible to use more CPU threads
    //   than there are CUDA devices, in which case several CPU
    //   threads will be allocating resources and launching kernels
    //   on the same device.  For example, try omp_set_num_threads(2*num_gpus);
    //   Recall that all variables declared inside an "omp parallel" scope are
    //   local to each CPU thread
    //
    // 通过获知GPU的数量来设定下面参与工作的线程数量
    omp_set_num_threads(num_gpus);  // create as many CPU threads as there are CUDA devices
    //omp_set_num_threads(2*num_gpus);// create twice as many CPU threads as there are CUDA devices
    //自动创建 num_gpus 个线程
    #pragma omp parallel
    {
        //每个线程获得自己的线程号,从0开始计数
        unsigned int cpu_thread_id = omp_get_thread_num();
        //获取 openmp 自动创建的总的线程数
        unsigned int num_cpu_threads = omp_get_num_threads();
	    printf("cpu_thread_id=%u   num_cpu_threads=%u\n", cpu_thread_id, num_cpu_threads);

        // set and check the CUDA device for this CPU thread
        int gpu_id = -1;
        //两个或以上的线程,可以同时使用同一个 gpu 设备;cudaSetDevice(id) 会使得本线程锁定这个 id-th gpu 设备,接下来发生的cudaXXX,都是在这个gpu上发生的。
        checkCudaErrors(cudaSetDevice(cpu_thread_id % num_gpus));   // "% num_gpus" allows more CPU threads than GPU devices
        checkCudaErrors(cudaGetDevice(&gpu_id));//这个函数仅仅返回本线程锁定的gpu的id。
        printf("CPU thread %d (of %d) uses CUDA device %d, set id=%d, get id = %d\n", cpu_thread_id, num_cpu_threads, gpu_id, cpu_thread_id%num_gpus, gpu_id);

        int *d_a = 0;   // pointer to memory on the device associated with this CPU thread
        //下面找到本线程需要处理的数据起始地址
        int *sub_a = a + cpu_thread_id * n / num_cpu_threads;   // pointer to this CPU thread's portion of data
        unsigned int nbytes_per_kernel = nbytes / num_cpu_threads;//其中的 nbytes = num_gpus * [8192*sizeof(int)], 是整除关系;得到每个线程需要分配的显存字节数
        dim3 gpu_threads(128);  // 128 threads per block
        dim3 gpu_blocks(n / (gpu_threads.x * num_cpu_threads));// n = num_gpus * 8192;   其中 8192 是128的整数倍, num_cpu_threads 是 num_gpus 的小小的整数倍; 
        // 这样每个block含128个线程; 每个线程需要几个block呢 = n/(gpu_threads.x * num_cpu_threads)

        checkCudaErrors(cudaMalloc((void **)&d_a, nbytes_per_kernel));//在本线程所setDevice的gpu上cudaMalloc显存空间;
        checkCudaErrors(cudaMemset(d_a, 0, nbytes_per_kernel));//将分的的空间刷0
        // 将本线程需要处理的数据块拷贝的本线程所cudaMalloc的显存中;
        checkCudaErrors(cudaMemcpy(d_a, sub_a, nbytes_per_kernel, cudaMemcpyHostToDevice));
        //本线程启动kernel,处理自己的显存的数据;
        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);
}




运行效果:


要保证cuda lib的线程安全,首先确定cuda driver cuda runtime 都是线程安全的,这个其实是没有问题的,也就是说多个线程多块显卡同时运行的场景是不会引发cuda runtime的线程安全问题的,这点可以放心;

3, 多线程安全分析

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

DTS认证

一、什么叫DTS DTS 是“Digital Theatre System“的缩写&#xff0c;是”数字化影院系统“的意思。是一种音频格式&#xff0c;从技术上讲&#xff0c;把音效数据存储到另外的CD-ROM中&#xff0c;使其与影像数据同步。这样不但空间得到增加&#xff0c;而且数据流量也可以相对…

如何运用gpt改写出高质量的文章 (1)

大家好&#xff0c;今天来聊聊如何运用gpt改写出高质量的文章 (1)&#xff0c;希望能给大家提供一点参考。 以下是针对论文重复率高的情况&#xff0c;提供一些修改建议和技巧&#xff1a; 如何运用GPT改写出高质量的文章 一、引言 随着人工智能技术的飞速发展&#xff0c;自然…

HHDESK右键管理简介

在HHDESK管理文件&#xff0c;除了基本的打开、删除、复制、粘贴、重命名外&#xff0c;还有多种便捷编辑方式。 可以分别以下列模式打开文档&#xff1a; 文本模式即是以文本编辑器打开文档。 1 二进制模式 可进行二进制编辑。 2 JSON模式 可对JSON文件进行直观的解析…

b样条原理与测试

为了保留贝塞尔曲线的优点&#xff0c;同时克服贝塞尔曲线的缺点&#xff0c;b样条在贝塞尔曲线上发展而来&#xff0c;首先来看贝塞尔曲线的定义&#xff1a; 对于贝塞尔中的基函数而言&#xff0c;是确定的&#xff0c;全局唯一的&#xff0c;这导致了如果控制点发生变换将会…

软件测试--selenium安装使用

安装selenium不少人使用pip命令来安装selenium&#xff0c;辛辛苦苦安装完之后&#xff0c;还是不能使用。所以我们可以是直接使用编译器&#xff0c;pycharm直接安装selenium扩展包。 file中点击settings 在Settings中点击Project Interpreter,点击加号就可以安装各种需要的扩…

11.30BST理解,AVL树操作,定义;快速幂,二分求矩阵幂(未完)

完全二叉树结点的度可能有1&#xff0c;满二叉树的度只能为0或2 BST构建 BST是左孩子都比根节点小&#xff0c;右孩子都比根节点大 二叉搜索树的插入&#xff0c;删除&#xff0c;调整 平衡树理解 任何一个平衡二叉树&#xff0c;它的中序遍历都是一样的&#xff0c;都是有…

PRCD-1229 : An attempt to access configuration of database

今天维护oda一体机时&#xff0c;发现无法在grid用户下面关闭数据库实例&#xff0c;如下 ASM1:/home/gridoda0>srvctl stop database -d orcl -o immeidate PRCD-1229 : An attempt to access configuration of database orcl was rejected because its version 11.2.0.4.…

SpringBoot Seata 死锁问题排查

现象描述&#xff1a;Spring Boot项目&#xff0c;启动的时候卡住了&#xff0c;一直卡在那里不动&#xff0c;没有报错&#xff0c;也没有日志输出 但是&#xff0c;奇怪的是&#xff0c;本地可以正常启动 好吧&#xff0c;姑且先不深究为什么本地可以启动而部署到服务器上就无…

【代码随想录刷题】Day20 二叉树06

文章目录 1.【654】最大二叉树1.1 题目描述1.2 解题思路1.3 java代码实现1.4 总结 2.【617】合并二叉树2.1 题目描述2.2 解题思路2.3 java代码实现 3.【700】二叉搜索树中的搜索3.1 题目描述3.2 解题思路3.3 java代码实现 4.【98】验证二叉搜索树4.1 题目描述4.2 解题思路4.3 j…

卷积神经网络18种有效创新方法汇总,涵盖注意力机制、空间开发等7大方向

作为深度学习中非常重要的研究方向之一&#xff0c;卷积神经网络&#xff08;CNN&#xff09;的创新不仅表现在提升模型的性能上&#xff0c;还更进一步拓展了深度学习的应用范围。 具体来讲&#xff0c;CNN的创新架构和参数优化可以显著提高模型在各种任务上的性能。例如&…

如何跑AI模型—ultralytics

这里以跑 ultralytics 为示例&#xff0c;记录了如何从 0-1 跑个简单的模型&#xff0c;包括环境搭建。我的是 Window 系统&#xff0c;其他系统也类似。 主要流程是环境搭建&#xff0c;找个官网的 demo&#xff0c;收集好所需素材&#xff08;模型&#xff0c;图片等&#x…

Python入门第1篇

前言 很久之前就知道有python这个东西&#xff0c;当时也想的学学&#xff0c;不过一直没做行动派。 那时候就听说用python进行Excel数据分析处理、爬虫等很是厉害&#xff0c;但是始终没有与python的关系更进一步。 Python简介 用我自己的话说&#xff0c;python也是一门面…

k8s上安装KubeSphere

安装KubeSphere 前置环境安装nfs-server文件系统配置nfs-client配置默认存储创建了一个存储类metrics-server集群指标监控组件 安装KubeSphere执行安装查看安装进度 前置环境 下载配置我都是以CentOS 7.9 安装 k8s(详细教程)文章的服务器作为示例&#xff0c;请自行修改为自己的…

【评测脚本】机器信息评测(初版)

背景 QA的实际工作过程中,除了业务相关的测试外,也会涉及到一些评测相关的工作,甚至还要做多版本、多维度的评估分析。尤其是现在火热的大模型,相关的评测内容更是核心中的核心。当然本文的内容只是做一些初级的机器相关的评测信息,更多更广的评测需要更多时间的积累和总…

插入排序——直接插入排序和希尔排序(C语言实现)

文章目录 前言直接插入排序基本思想特性总结代码实现 希尔排序算法思想特性总结代码实现 前言 本博客插入排序动图和希尔排序视频参考大佬java技术爱好者&#xff0c;如有侵权&#xff0c;请联系删除。 直接插入排序 基本思想 直接插入排序是一种简单的插入排序法&#xff…

042:el-table表格表头自定义高度(亲测好用)

第042个 查看专栏目录: VUE ------ element UI 专栏目标 在vue和element UI联合技术栈的操控下&#xff0c;本专栏提供行之有效的源代码示例和信息点介绍&#xff0c;做到灵活运用。 &#xff08;1&#xff09;提供vue2的一些基本操作&#xff1a;安装、引用&#xff0c;模板使…

PairLIE论文阅读笔记

PairLIE论文阅读笔记 论文为2023CVPR的Learning a Simple Low-light Image Enhancer from Paired Low-light Instances.论文链接如下&#xff1a; openaccess.thecvf.com/content/CVPR2023/papers/Fu_Learning_a_Simple_Low-Light_Image_Enhancer_From_Paired_Low-Light_Instan…

解决Eslint和Prettier关于三元运算符的冲突问题

三元运算符Prettier的格式化 三元运算符Eslint的格式要求 解决办法 // eslint加入配置&#xff0c;屏蔽标红报错indent: [error, 2, { ignoredNodes: [ConditionalExpression] }]效果

class037 二叉树高频题目-下-不含树型dp【算法】

class037 二叉树高频题目-下-不含树型dp【算法】 code1 236. 二叉树的最近公共祖先 // 普通二叉树上寻找两个节点的最近公共祖先 // 测试链接 : https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/ package class037;// 普通二叉树上寻找两个节点的最近…

上个月暴涨34.6%后,SoundHound AI股票现在还能买入吗?

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 揭开SoundHound AI股价波动的原因 S&P Global Market Intelligence的数据显示&#xff0c;在摆脱了10月份的大幅下跌后&#xff0c;SoundHound AI的股价在11月份实现了34.6%的涨幅。 原因是该公司公布了稳健的第三季…
最新文章