【C++】CUDA期末复习指南下(详细)

2024-03-11 1063阅读

温馨提示:这篇文章已超过411天没有更新,请注意相关的内容是否还可用!

🍎 博客主页:🌙@披星戴月的贾维斯

🍎 欢迎关注:👍点赞🍃收藏🔥留言

🍇系列专栏:🌙 C/C++专栏

🌙请不要相信胜利就像山坡上的蒲公英一样唾手可得,但是请相信,世界上总有一些美好值得我们全力以赴,哪怕粉身碎骨!🌙

🍉一起加油,去追寻、去成为更好的自己!

【C++】CUDA期末复习指南下(详细)

文章目录

  • 前言
  • 🍎1、cuda常考函数
  • 🍎2、CUDA编程
      • 🍇一个典型的CUDA程序的基本框架
      • 🍇简单的CUDA加法
      • 🍇获取计算机线程块的分配
      • 🍇在GPU任意长度的矢量求和
      • 🍇点积运算
      • 🍇常量内存光线跟踪(使用共享内存)
      • 🍇GPU使用一维纹理内存的热传导模拟计算
      • 🍇统计直方图(普通版本)
      • 🍇GPU原子递增操作统计直方图
      • 🍎总结

        提示:以下是本篇文章正文内容,下面案例可供参考


        前言

            上期CUDA期末复习指南我们主要讲了GPU的串行/并行以及一些背诵的知识点,这篇博客我们继续介绍cuda的函数以及cuda编程,常考的CUDA函数和编程题博主在这里为大家总结一下,希望对大家有所帮助。

        🍎1、cuda常考函数

          在上一节我们已经讲到了cudaFreeHost这个函数,是用来释放固定主机内存的。我们直接承接上一篇的内容

        8. cudaSetDevice

        cudaSetDevice 函数用于设置当前线程要使用的 CUDA 设备。它的函数原型为

        cudaError_t cudaSetDevice (
        int device
        );
        

        其中, device 参数表示要使用的 CUDA 设备的编号,编号从 0 开始。

        使用方法如下代码:

        int main()
        {
        int device = 0;
        cudaSetDevice(device);
        return 0;
        }
        
        1. cudaDeviceReset(这个函数用得较少)

          cudaDeviceReset 是 CUDA 运行时 API 中的一个函数,用于重置当前设备的状态。该函数执行后,会清除当前设备上的所有运行时状态,并释放所有与该设备有关的资源。它的函数原型为:

        cudaError_t cudaDeviceReset(void); // 该函数不接受任何参数
        
        1. cudaHostAlloc

          cudaHostAlloc 是 CUDA 中用于在主机(Host)上分配内存的函数。与普通的 malloc 或 new不同,通过 cudaHostAlloc 分配的主机内存可以与设备(Device)之间进行零拷贝(Zerocopy)操作,这意味着可以在主机和设备之间非常高效地共享数据。它的函数原型为:

        cudaError_t cudaHostAlloc(
        void **ptr,
        size_t size,
        unsigned int flags
        );
        

        其中, ptr 为分配的内存的指针所存放的地址; size :分配的内存的大小,以字节为单位;flags :分配内存的标志,可以是以下任意组合:

        cudaHostAllocDefault :默认标志,表示按照系统的最佳准则分配主机内存。

        cudaHostAllocPortable :可以在不同 CUDA 设备之间交换的可移植内存。

        cudaHostAllocMapped :将主机内存映射到设备内存,以实现零拷贝操作。

        cudaHostAllocWriteCombined :对写缓冲区进行优化的内存,用于使用

        cudaMemcpyAsync 在主机和设备之间高效地复制内存。

        使用方法如下代码:

        int main()
        {
        int n = 1024;
        float *data;
        HANDLE_ERROR( cudaHostAlloc((void**)&data, n * sizeof(float),
        cudaHostAllocMapped) );
        // ... 使用 data 在主机和设备之间进行零拷贝操作 ...
        cudaFreeHost(data);
        return 0;
        }
        
        1. cudaHostGetDevicePointer

          cudaHostGetDevicePointer 函数是 CUDA 主机内存和设备内存之间数据传输的重要函数之一。

          该函数的作用是将主机内存指针映射到设备内存地址空间中,并返回设备内存的指针。这样,就可

          以在主机内存和设备内存之间直接传递数据,从而避免了在主机内存和设备内存之间复制数据的开

          销,提高了程序运行效率。它的函数原型为:

        cudaError_t cudaHostGetDevicePointer(
        void **pDevice,
        void *pHost,
        unsigned int flags // 此处需要被置为0
        );
        

        其中, pDevice 是指向设备内存指针的指针; pHost 是主机内存指针; flags 是标志位,用于控制映射的行为。

        与 cudaHostAlloc 搭配使用方法如下:

        int main()
        {
        float *h_ptr, *d_ptr;
        size_t size = N * sizeof(float);
        // 分配 CUDA 主机内存
        HANDLE_ERROR( cudaHostAlloc(&h_ptr, size, cudaHostAllocDefault) );
        // 在设备上分配内存
        HANDLE_ERROR( cudaMalloc((void**)&d_ptr, size) );
        // 映射设备内存
        HANDLE_ERROR( cudaHostGetDevicePointer(&d_ptr, h_ptr, 0) );
        // 将数据从主机内存复制到设备内存
        HANDLE_ERROR( cudaMemcpy(d_ptr, h_ptr, size, cudaMemcpyHostToDevice) );
        // 对设备上的数据进行操作
        SomeKernel(d_ptr, N);
        // 将数据从设备内存复制到主机内存
        HANDLE_ERROR( cudaMemcpy(h_ptr, d_ptr, size, cudaMemcpyDeviceToHost) );
        // 释放内存
        HANDLE_ERROR( cudaFree(d_ptr) );
        HANDLE_ERROR( cudaFreeHost(h_ptr) );
        return 0;
        }
        
        1. cudaGetErrorString

          cudaGetErrorString 是一个 CUDA 函数,它可以将 CUDA 错误码转换为可读字符串,方便开发者调试和查错。它的函数原型为:

        cudaError_t cudaGetErrorString(
        cudaError_t error,
        const char **pStr);
        

        其中, error 为要转换为字符串的 CUDA 错误码; **pStr 为指向指针的指针,用于存储转换后的字符串。用法如下:

        使用方法如下:

        cudaError_t err = cudaMalloc(&devPtr, size);
        if (err != cudaSuccess)
        {
        printf("CUDA error: %s\n", cudaGetErrorString(err));
        }
        
        1. cudaMemcpyToSymbol

          cudaMemcpyToSymbol 将 count 个字节从 src 指向的内存复制到 symbol 指向的内存中,这个变量存放在设备的 全局内存或常量内存中。

          使用方法如下:

        __constant__ Sphere s[SPHERES]; // 在全局处定义
        int main()
        {
        Sphere *temp_s = (Sphere*)malloc( sizeof(Sphere) * SPHERES);
        // 在局部动态拷贝
        HANDLE_ERROR( cudaMemcpyToSymbol( s, temp_s, sizeof(Sphere) * SPHERES));
        return 0;
        }
        
        1. cudaEventCreate(重点)

          cudaEventCreate 是一个CUDA运行时API函数,用于创建CUDA事件。CUDA事件可以被用于测量GPU程序的执行时间、在异步操作间同步数据等。它的函数原型为:

        cudaError_t cudaEventCreate (
        cudaEvent_t* event
        );
        

        其中, event 是一个指向 cudaEvent_t 类型对象的指针。

        使用方法如下:

        int main()
        {
        cudaEvent_t start, stop;
        cudaEventcreate(&start);
        cudaEventcreate(&stop);
        }
        
        1. cudaEventRecord

          cudaEventRecord 是一个 CUDA 运行时函数,用于记录一个调用 cudaEventCreate 创建的CUDA 事件的时间点。通常情况下,它用作测量时间间隔或异步操作的同步。

        2. cudaEventSynchronize、

          cudaEventSynchronize 函数用于等待一个事件完成,并阻塞当前主机线程,直到该事件完成为止。它接收一个事件作为参数,并会一直等待该事件完成,直到可以安全地使用该事件依赖的所有CUDA 内存和其他资源。

        3. cudaEventElapsedTime

          cudaEventElapsedTime 函数用于计算两个事件记录的时间间隔,该函数返回两个事件之间的时间间隔(以毫秒为单位)。

        4. cudaEventDestroy

          cudaEventDestroy 函数用于销毁一个 CUDA 事件对象,释放与之关联的资源。

          使用方法如下:

        int main()
        {
        cudaEvent_t start, stop;
        HANDLE_ERROR( cudaEventCreate(&start) );
        HANDLE_ERROR( cudaEventCreate(&stop) );
        // 记录开始时间点
        HANDLE_ERROR( cudaEventRecord(start, 0) );
        // 执行核函数
        myKernel >(...);
        // 记录结束时间点
        HANDLE_ERROR( cudaEventRecord(stop, 0) );
        // 等待GPU操作完成
        HANDLE_ERROR( cudaEventSynchronize(stop) );
        float elapsedTime;
        // 计算两个时间点之间经过的时间间隔
        HANDLE_ERROR( cudaEventElapsedTime(&elapsedTime, start, stop) );
        printf("Execution time: %f ms\n", elapsedTime);
        // 销毁已经创建的事件对象
        HANDLE_ERROR( cudaEventDestroy(start) );
        HANDLE_ERROR( cudaEventDestroy(stop) );
        return 0;
        }
        
        1. atomicAdd

          atomicAdd 是 CUDA 提供的一种原子操作函数,用于在 GPU 全局内存中进行原子加操作。它的、作用是确保在并发情况下,对该内存地址执行原子加操作,以避免数据争用的问题,使得并发访问的结果能够正确累加。

        🍎2、CUDA编程

        🍇一个典型的CUDA程序的基本框架

        1、头文件包含
        2、常量定义
        3、C++自定义函数和CUDA核函数声明
        4、int main (void)
        {
        	定义主机和设备变量/数组。
        	分配主机和设备内存
        	初始化主机中的数据
        	将某些数据从主机复制到设备
        	调用核函数在设备中计算
        	将计算结果从设备中拷贝回主机变量/数组。
        	释放主机/设备内存
        }
        C++自定义函数和CUDA核函数实现(定义)
        

        🍇简单的CUDA加法

        #include
        #include
        #include
        //定义global类型的函数
        __global__ void add(int *const c, int const a, int const b)
        {
            *c = a + b;
        }
        int main ()
        {
            int c;
            int *dev_c;
            //申请显存
            cudaMalloc((void**)&dev_c, sizeof(int));
            //调用核函数
            addadd(dev_c, 2, 7);
            //把在dev_c的结果拷贝回cpu变量c中
            cudaMemcpy(&c, dev_c, sizeof(int), cudaMemcpyDeviceToHost);
            printf("%d\n", c);
            //释放显存
            cudaFree(dev_c);
            return  0;
        }
        

        🍇获取计算机线程块的分配

        一个简短内核程序输出线程块、线程、线程束和线程全局标号到屏幕上,了解线程块的分配。

        #include
        #include
        __global__ void what_is_my_id(unsigned int* const block, unsigned int* const thread,
                        unsigned int* const warp, unsigned int* const calc_thread){
            const unsigned int thread_idx = blockIdx.x * blockDim.x + threadIdx.x; 
            //其中这个thread_idx是当前线程的坐标
            block[thread_idx] = blockIdx.x;//当前线程位于的线程块
            thread[thread_idx] = threadIdx.x;//当前线程的索引
            warp[thread_idx] = threadIdx.x / warpSize;//当前线程的宽度
            calc_thread[thread_idx] = thread_idx;//当前线程在整个网格中的索引
        }
        #define ARRAY_SIZE 128
        #define ARRAY_SIZE_IN_BYTES (sizeof(unsigned int) * (ARRAY_SIZE))
        unsigned int cpu_block[ARRAY_SIZE];
        unsigned int cpu_thread[ARRAY_SIZE];
        unsigned int cpu_warp[ARRAY_SIZE];
        unsigned int cpu_calc_thread[ARRAY_SIZE];
        int main ()
        {
            //确定调用核函数的多少
            const unsigned int num_blocks = 2;
            const unsigned int num_threads = 64;
            //定义gpu线程块数组
            unsigned int* gpu_block;
            unsigned int* gpu_thread;
            unsigned int* gpu_warp;
            unsigned int* gpu_calc_thread;
            //申请显存
            cudaMalloc((void **)&gpu_block, ARRAY_SIZE_IN_BYTES);
            cudaMalloc((void **)&gpu_thread, ARRAY_SIZE_IN_BYTES);
            cudaMalloc((void **)&gpu_warp, ARRAY_SIZE_IN_BYTES);
            cudaMalloc((void **)&gpu_calc_thread, ARRAY_SIZE_IN_BYTES);
            //调用核函数
            what_is_my_id(gpu_block, gpu_thread,gpu_warp,gpu_calc_thread);
            
            //再把显存内容拷贝回cpu的数组
            cudaMemcpy(cpu_block, gpu_block,ARRAY_SIZE_IN_BYTES,cudaMemcpyDeviceToHost);
            cudaMemcpy(cpu_thread, gpu_thread,ARRAY_SIZE_IN_BYTES,cudaMemcpyDeviceToHost);
            cudaMemcpy(cpu_warp, gpu_warp,ARRAY_SIZE_IN_BYTES,cudaMemcpyDeviceToHost);
            cudaMemcpy(cpu_calc_thread, gpu_calc_thread,ARRAY_SIZE_IN_BYTES,cudaMemcpyDeviceToHost);
            //释放指针
            cudaFree(gpu_block);
            cudaFree(gpu_thread);
            cudaFree(gpu_warp);
            cudaFree(gpu_calc_thread);
            //打印数据
            for(int i = 0; u  
        

        🍇在GPU任意长度的矢量求和

        #include
        #include
        #include
        #define N (65536 * 128 * 10)
        __global__ void add(int* a, int *b, int *c)
        {
            int tid = blockIdx.x * blockDim.x + threadIdx.x;
            while(tid  
        

        🍇点积运算

        矢量的点积运算(Dot Product,也称为内积)。这个计算包含两个步骤。首先把两个输入矢量中相应的元素相乘,类似矩阵加法,不过使用的乘法操作。其次把计算出来的值相加起来得到一个标量输出值(单个数值)。

        (x1,x2,x3,x4)·(y1,y2,y3,y4) = x1y1+x2y2+x3y3+x4y4

        先像矢量加法那样,每个线程将两个相应的元素相乘,每个线程保存计算的乘积的求和。

        代码示例:

        #include"../common/book.h"
        #define imin (a,b) (a  
        

        🍇常量内存光线跟踪(使用共享内存)

        【C++】CUDA期末复习指南下(详细)

        以光线跟踪为例,从三维对象场景中生产二维图像的一种方式。需要判断图像中的每个像素的颜色和强度。

        一组包含球状物体的场景,从每个像素发射一道光线,跟踪这些光线会命中哪些球面,当一道光线穿过多个球面时,只有最接近的球面才有机会被看到。

        对球面进行数据结构建模,包含了球面中心点(x,y,z),半径radius,颜色值(r,g,b)

        代码示例:

        #include "cuda_runtime.h"
        #include "device_launch_parameters.h"
        #include "H:\cuda_by_example\common\book.h"
        #include "H:\cuda_by_example\common\cpu_bitmap.h"
        #include "device_functions.h"
        #include 
        #define DIM 1024  
          
        #define rnd( x ) (x * rand() / RAND_MAX)  
        #define INF     2e10f  
          
        struct Sphere {  
            float   r,b,g;  
            float   radius;  
            float   x,y,z;  
            __device__ float hit( float ox, float oy, float *n ) {  
                float dx = ox - x;  
                float dy = oy - y;  
                if (dx*dx + dy*dy bitmap;
        	//由于tex是全局并且是有界的,因此我们必须通过一个标志来来选择每次迭代中哪个是输入/输出
        	volatile bool dstOut = true;
        	for (int i = 0; i dev_inSrc;
        			out = d->dev_outSrc;
        		}
        		else
        		{
        			in = d->dev_outSrc;
        			out = d->dev_inSrc;
        		}
        		copy_const_kernal  > (in);
        		blend_kernal  > (out,dstOut);
        		dstOut = !dstOut;
        	}
        	//将float数值映射成颜色,以便用图像显示它
        	float_to_color  >(d->output_bitmap, d->dev_inSrc);
        	cudaMemcpy(bitmap->get_ptr(), d->output_bitmap, bitmap->image_size(), cudaMemcpyDeviceToHost);
        	cudaEventRecord(d->stop, 0);
        	cudaEventSynchronize(d->stop);
        	float elapsedTime;
        	cudaEventElapsedTime(&elapsedTime, d->start, d->stop);
        	d->totalTime += elapsedTime;
        	++d->frames;
        	printf("Average Time per frame:%3.1fms\n", d->totalTime / d->frames);
        }
        void anim_exit(DataBlock *d)
        {
        	cudaUnbindTexture(texIn);
        	cudaUnbindTexture(texOut);
        	cudaUnbindTexture(texConstSrc);
        	cudaFree(d->dev_constSrc);
        	cudaFree(d->dev_inSrc);
        	cudaFree(d->dev_outSrc);
        	cudaEventDestroy(d->start);
        	cudaEventDestroy(d->stop);
        }
        

        🍇统计直方图(普通版本)

        #include "../common/book.h"
        #define Size   (100*1024*1024)
        int main()
        {
            unsigned char *buffer = (unsigned char*)big_random_block(Size);
            clock_t start, stop;
            start = clock();
            
            unsigned int histo[256];
            for(int i = 0; i  
        

        🍇GPU原子递增操作统计直方图

        代码示例:

        #include "../common/book.h"
        #define Size   (100*1024*1024)
        __global__ void histo_kernel(unsigned char *buffer,long size,unsigned int *histo){
            int i = threadIdx.x + blockIdx.x * blockDim.x;
            int stride = blockDim.x * gridDim.x;
            while(i  
        

        🍎总结

            本文总共总结多个常考的函数和编程题,希望对大家学习和复习CUDA有所帮助,感谢大家的支持,一起进步!

VPS购买请点击我

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

目录[+]