首页 > 编程语言 >CUDA入门必看,如何高效地编写并行程序

CUDA入门必看,如何高效地编写并行程序

时间:2024-08-10 22:49:14浏览次数:14  
标签:函数 必看 并行程序 线程 CUDA GPU block 内存

CUDA入门必看,如何高效地编写并行程序

进入公司实习已经一个月有余,从编写第一个 kernel 开始到现在分析优化 LLM 程序,我的CUDA学习成果颇丰,项目进展顺利,现将我的学习路径整理分享出来。跟随在GPU芯片架构领域深耕多年的企业家王旭老师,我从一开始对GPU架构领域全无了解,到如今上手LLM开源项目优化项目,在该领域我逐渐从一个的小白成长为资深小白。从一个企业级程序架构师的角度,王旭老师为我整理了一套CUDA学习方案和文档资料。

虽然CUDA架构是基于C++语言所开发的,但它不仅仅只是在GPU调用、线程分配方向上API的延伸拓展,而是遵循着一定的设计思路。CUDA的研发以及在当下的流行,从始至终都在完成提升性能这一件事。从这一套学习方案中,你会时刻感受到性能指标在编写CUDA程序中所占据的考量有多重。因此要编写出更加成熟的kernel程序,可以跟着我梳理出的学习流程一步一步掌握基础知识,侧重于对性能指标的提升,将CUDA最开始的初衷运用到你的代码之中。

要学习它最前沿的知识,就先了解它最初的样貌。

学习CUDA我推荐先从Nvidia CUDA 官方文档开始。我将我的学习路线顺序整理如下,并对每一章节的知识作出归纳和总结,并附上我对初学者所给出的建议,便于你理解每一篇文档的用意,所要达到的目的,帮助你快速高效地攻克在学习CUDA道路上的重重难点,获得知识的同时在理念上的有所感悟。

我将不对代码细节上做过多的讲述,如果你需要的是保姆级教程, 也许这篇文档并不适合你。但是如果你在细节上遇到许多坑,那么这篇文档将为你提供解决问题的思路。

1.Cuda 简介

主题:分配GPU线程 & 调度GPU内存

原文链接: An Even Easier Introduction to CUDA

如果你在耕地,你需要什么?一头壮牛还是1024只鸡?

你只需要在 main 函数中敲入这一行简单的代码,就能体会到调用GPU函数是一件多么自然的事。

kernel<<<grid, block>>>(char* param);

其中kernel是你的函数名,你可以自定义。在初学阶段,你可以将grid设置为 1 ,代表你只用到一个block线程块。block是你的线程块尺寸,代表着一个线程块中线程thread的多少。gridblock可以是一维的,也可以是二维的,甚至是三维的,取决于你要完成的任务。在上图的例子中,你可以将block简单设置为256。你知道它最好是2的幂次(但<=1024),它不仅仅只是程序员的强迫症

在你的kernel函数实现前加上 __global__,代表它是运行在GPU的函数。并且你的每一个线程中都在调用这个函数。

__global__ kernel(char* param) { ... }

为了能够在不同的线程中处理不同的数据,假设现在有一块内存地址,我需要在线程中拿到这些内存地址,访问它的数据。那么我们这样做,给每个线程分配一个id序号,它们从0 ~ n,n是id数最大的线程,其实在一维线程块中它等于上面的block - 1,即255。我们用这些序号的差异来计算得出每个线程需要访问的地址。

thread & block

在CUDA语言中,我们一般称GPU为设备Device,称CPU为主机Host。假设我们的Device上有一块内存 d_mem , 我们这样来访问到它的第 0 ~ 255 个数据。

__global__ kernel(int* d_mem) { 

	int id =  treadIdx.x;
	d_mem[id] = 0;

}

这个函数在每一个线程中都会被调用。此时想必你已经知道了如何使用线程的序号来控制你想要访问的内存位置。下面的几个API是我简单引用的最基础的API。

blockIdx.x  //此线程的block序号
blockDim.x  //block在x维上的长度,在一维block中代表着一个线程块中的thread数目
gridDim.x   //grid在x维上的长度,在一维grid中代表着一个grid中的block数目

经过学习,你会从一维的block/grid逐步接触到二维、三维的block/grid,但现在还不必要。请认真思考这些变量的含义,并把它们抽象成图格的形式,这样你会更加理解其中的含义。

上图中的index的含义是什么?以后你将经常用到这样的形式去处理更多的数据。index = blockIdx.x * blockDim.x + threadIdx.x。这里我需要做一点提醒,上图中的gridDim.x是线程块block的长度,也就是说有 4096 个 0~255 那么长的block曾经我误以为这是 thread的数目。


2.GPU-CPU共用内存:统一内存

主题:统一内存调度 & 内存创建销毁、拷贝

原文链接: Unified Memory for CUDA Beginners

程序员在某种意义上也属于搬运工

当你需要使用线程处理数据的时候,一定离不开内存空间的创建。在普通的C语言程序中,我们只需要调用动态分配内存的函数 malloc 就能在内存中开辟一块指定大小的内存空间。但是 malloc 所创建的内存空间是 Host 端的,你的 Decive 一般来说是无法访问的。 CUDA 中提供了一个创建统一内存的API, 这个API的使用和 malloc 类似,但是创建的内存可以被 device 端调用。也就是说,你的__global__函数可以访问这块内存。

float* x;
float* y;

cudaMallocManaged(&x, N*sizeof(float));
cudaMallocManaged(&y, N*sizeof(float));

作为初学者你可以先使用它,而不去思考这块内存究竟被放置在哪个位置。 你可以通过文档继续了解它的实现原理,简单来说,尽管这个函数在 Hostdevice 上都创建了内存,但是只有当你需要在 device 上使用时,它才会自动将 Host 上的内存拷贝到 device 上。

CPU-GPU

以下是三个创建内存的API。

malloc()            //原生C语言的动态内存创建函数
cudamalloc()        //特别的,只在 Device 端创建内存
cudamallocHost()    //特别的,只在 Host 端创建内存

创建好内存,你需要对你的数据进行初始化, memset() 函数很有帮助。 想起曾经大一课堂上老师为我们介绍这类函数的用法,他以过来人告诉我们这类函数在实际编程经常使用到。 作为大一的愣头青,心是向着算法的,自然没把搬运内存拷贝内存的事放在心上。死去的记忆开始攻击我(ー`´ー),如今每天都在开辟内存、初始化内存、拷贝内存、再把内存拷贝回来。以下是几个内存拷贝的函数,memcpy()想必你已经不陌生, cudamemcpy()函数是cuda的内存拷贝API,其中第四个参数决定了内存从哪拷贝到哪。

memcpy(*dest, *src, byteSize)  //从src地址拷贝byteSize字节大小的内存给dest的地址
cudamemcpy(*dest, *src, byteSize, cudamemcpyHostToDevice); //从Host拷贝给Device
cudamemcpy(*dest, *src, byteSize, cudamemcpyDeviceToHost); //从Device拷贝回Host

如果你使用完刚才开辟的内存后顺手把它释放了,那么你是一个好程序员

Free(*mem);      //释放由 malloc 创建的内存
cudaFree(*mem);  //释放由 cudamalloc() cudamallocHost() 创建的内存

你已经是一个成熟的程序员了,现在你要完成组织交给你的任务ヾ(≧▽≦*)o。以下是一段完整的CUDA代码,其中的saxpy是运行在GPU上的kernel函数,它可以将 x, y 矩阵的对应的元素做一次乘加操作。 将这段代码复制到你的.cu文件里,通过编译运行它来直接感受kernel函数是如何被调用的,并了解我们是怎么在 Hostdevice 端进行数据传输的,我想这会对你有帮助。

#include <stdio.h>

__global__ void saxpy(int n, float a, float *x, float *y)
{
    int i = blockIdx.x * blockDim.x + threadIdx.x;
    if (i < n)
        y[i] = a * x[i] + y[i];
}

int main(void)
{
    int N = 20 * (1 << 20);
    float *x, *y, *d_x, *d_y;
    x = (float *)malloc(N * sizeof(float));
    y = (float *)malloc(N * sizeof(float));

    cudaMalloc(&d_x, N * sizeof(float));
    cudaMalloc(&d_y, N * sizeof(float));

    for (int i = 0; i < N; i++)
    {
        x[i] = 1.0f;
        y[i] = 2.0f;
    }

    cudaMemcpy(d_x, x, N * sizeof(float), cudaMemcpyHostToDevice);
    cudaMemcpy(d_y, y, N * sizeof(float), cudaMemcpyHostToDevice);

    saxpy<<<(N + 511) / 512, 512>>>(N, 2.0f, d_x, d_y);

    cudaMemcpy(y, d_y, N * sizeof(float), cudaMemcpyDeviceToHost);

    float maxError = 0.0f;
    for (int i = 0; i < N; i++)
    {
        maxError = max(maxError, abs(y[i] - 4.0f));
    }
    printf("Max error: %fn\n", maxError);

}

CUDA程序必须在nvcc的环境下编译运行,并且你需要把.cpp后缀名改为.cu。 建立nvcc的环境需要 Nvidia 的 GPU 。 你可以使用这两行代码再命令行中进行编译和运行。

nvcc test.cu -o test.out    //编译
./test.out                  //运行

上面的代码我直接抄自借鉴 Nvidia 的官方文档,并对其中目前学习未涉及的调用做了删改,以便你能专注于这一种计算方法。在这两章,我们学习了 CUDA 的线程分配、内存调度等基础知识,帮助你编写了第一个kernel程序,当然,我们并不会止步于此,我们的CUDA学习之旅才刚刚开始。

我将从CUDA的运行性能查询设备参数处理报错数据传输流控制等多个方面继续为你介绍CUDA的入门知识,多方面地带你感受CUDA的 编程之美


标签:函数,必看,并行程序,线程,CUDA,GPU,block,内存
From: https://www.cnblogs.com/beiwei31/p/18352897

相关文章

  • CUDA--内存访问越界或无效的索引操作解决办法
    报错信息File"D:\anaconda3\envs\HCAVE2\lib\site-packages\torch\nn\utils\rnn.py",line258,inpack_padded_sequencesorted_indices=sorted_indices.to(input.device)RuntimeError:CUDAerror:device-sideasserttriggeredCUDAkernelerrorsm......
  • CUDA--内存访问越界或无效的索引操作解决办法--总结
    设备端的断言错误(device-sideasserttriggered)通常发生在CUDA代码中访问无效的内存地址或执行了无效的操作。解决这种错误需要系统地排查代码中的潜在问题。以下是详细的解决方案:1.检查数组边界确保所有访问数组或指针的操作都在有效范围内。检查线程索引和块索引的计算,确......
  • 2024护网必看!日薪一千!怎么才能搞定(附零基础学习资料)
    前言你听说过护网吗?就是那个日薪1000——20000,食宿全包,干一个月顶半年,公安部牵头,用来评估企事业单位网络安全的活动!是不是有很多小伙伴已经心动了?要不我展开说说什么是护网行动?护网行动是一项由公安部牵头的,以检测企事业单位的网络安全防护能力为目的,针对全国范围......
  • opencv4.5 带cuda 安装
    opencv4.5cmake-DCMAKE_BUILD_TYPE=RELEASE-DCMAKE_CXX_FLAGS_RELEASE="-O3"-DCMAKE_INSTALL_PREFIX=/home/slam/Thirdpart/opencv-4.5.2/install-DOPENCV_EXTRA_MODULES_PATH=/home/slam/Thirdpart/opencv-4.5.2/opencv_contr......
  • AI绘画 Stable Diffusion后期处理—无需ControlNet也能轻松高清放大图像与老旧照片修
    大家好,我是设计师阿威分享了这么多期AI绘画StableDIffusion的入门教程和一些常用的插件玩法后,不知道大家有没有发现,SD还有一个功能,似乎没怎么用到过,它就是—后期处理。今天就给大家分享一下SD中的“后期处理”的常用玩法。后期处理可以选择「单张照片」、「批量处理......
  • 万字长文带你深度学习AI绘画工具 Stable Diffusion 保姆级实战,AI绘画入门必看实用性教
    大家好,我是设计师阿威今天给大家分享一下AI绘画工具StableDiffusion的实战教程,非常适合新手入门和巩固以往知识体系,同时我也准备了配套的学习资料,本教程没有难懂的理论,全是实操的截图,非常通俗易懂。本教程没有难懂的理论,全是实操的截图,非常通俗易懂。认真看完这篇教程,能......
  • Windows10下多版本CUDA的安装与切换
    文章目录一、前言二、安装CUDA1.确定是否支持所需的CUDA版本2.下载CUDA3.安装CUDA4.环境变量三、安装cudnn1.下载cudnn2.替换文件四、切换CUDA版本1.切换版本2.检查版本是否切换成功参考一、前言  当我们跑深度学习的代码时,有时......
  • 如何防范数据泄露【必看干货】:防止数据泄露的方法有哪些
    在数字化时代,数据已成为企业运营的核心资产,其安全性直接关系到企业的竞争力和声誉。随着网络威胁的日益复杂和多样化,数据防泄漏系统(DLP)成为了企业保护敏感信息不被非法获取、使用或泄露的关键工具。本文将深入探讨数据防泄漏系统的基本原理、几种常见的防泄漏方式,并重点介绍企......
  • 计算机毕业设计必看必学!! 87229 基于ssm珠宝店信息管理系统,原创定制程序, java、PHP
    摘要近年来,随着移动互联网的快速发展,电子商务越来越受到网民们的欢迎,电子商务对国家经济的发展也起着越来越重要的作用。简单的流程、便捷可靠的支付方式、快捷畅通的物流快递、安全的信息保护都使得电子商务越来越赢得网民们的青睐。现今,大量的计算机技术应用于商业领域,......
  • 计算机毕业设计必看必学! ! 79197 基于ssm+mysql的学生心理健康在线咨询平台,原创定制
    摘要:在社会快速发展的影响下,教育业继续发展,大大增加了学生心理健康在线咨询平台的数量、多样性、质量等等的要求,使学生心理健康在线咨询平台的管理和运营比过去十年更加困难。依照这一现实为基础,设计一个快捷而又方便的学生心理健康在线咨询平台是一项十分重要并且有价值的事......