【CUDA】常量内存

news/2025/2/9 13:20:34/

目录

一、认识常量内存

二、使用常量内存实现1D模板

三、与只读缓存比较


一、认识常量内存

常量内存对内核代码而言是只读的,但它对主机而言即是可读的又是可写的。常量内存位于设备的DRAM上(和全局内存一样)。有一个专用的片上缓存,从每个SM的常量缓存中读取的延迟,比直接从常量内存中读取要低得多。每个SM常量内存缓存大小的限制为64KB

constant Memory 的获取方式不同于其它的 GPU 内存,对于 constant Memory 来说,最佳获取方式是 warp 中的 32 个thread获取 constant Memory 中的同一个地址。若 warp 中的线程访问不同的地址,那么访问就需要串行

一个常量内存读取的成本与 warp 中 thread 读取唯一地址的数量呈线性关系。在全局作用域中必须用以下修饰符声明常量内存:__constant__。常量内存变量的生存期与应用程序的生存期相同,其对于网格内的所有线程都是可访问的,并且主机可以通过运行时函数访问

当使用 CUDA 独立编译能力时,常量内存变量跨多个源文件可见。因为设备只能读取常量内存,所以常量内存中的值必须使用以下运行时函数进行初始化:

cudaError_t cudaMemcpyToSymbol(const void *symbol, const void *src, size_t count, size_t offset, cudaMemcpyKind kind);

cudaMemcpyToSymbol 函数将 src 指向的数据复制到设备上有 symbol 指定的常量内存中。枚举变量 kind 指定了传输方向,默认情况下,kind是 cudaMemcoyHostToDevice

二、使用常量内存实现1D模板

在一维中,在位置 x 周围九点模板会给这些位置上的值应用一些函数:{x-4h,x-3h,x-2h,x-h,x,x+h,x+2h,x+3h,x+4h}

一个九点模板的例子是实变量函数 f 在点 x 上一阶导数的第八阶中心差分公式。裂解这个公式的应用并不重要,只要简单的了解到其会将上述的九点作为输入并产生单一输出。接下来将这个公式作为一个示例模板:

那么要放到 constant Memory 中的便是其中的 c0、c1、c2 ... ...

const int RADIUS = 4;  // 半径, 一个数左右两边各4个数, 组成9个数 
const int LENGTH = 6; // 共有多少有效数字// 在常量内存中声明coef数组
__constant__ float coef[RADIUS + 1];void set_coef_constant()
{const float host_coef[] = {0, 1, 2, 3, 4};cudaMemcpyToSymbol(coef, host_coef, ((RADIUS + 1) * sizeof(float)));
}__global__ 
void stencil_1d(float* input, float* output)
{// 因为每个线程需要 9 个点来计算一个点,所以使用共享内存来优化缓存数据,从而减少对全局内存的冗余访问__shared__ float data[LENGTH + 2 * RADIUS];// 访问全局内存的线程索引int globalThreadIndex = blockIdx.x * blockDim.x + threadIdx.x; // 0 到 5// 访问共享内存的线程索引int sharedThreadIndex = threadIdx.x + RADIUS; // 4 到 9// gloabl -> shareddata[sharedThreadIndex] = input[globalThreadIndex + RADIUS];// 前四个线程负责 读取左右边界if(threadIdx.x < RADIUS) {data[sharedThreadIndex - RADIUS] = input[sharedThreadIndex - RADIUS];data[LENGTH + sharedThreadIndex] = input[LENGTH + sharedThreadIndex];}__syncthreads();float tmp = 0;// 提示 CUDA 编译器,表明这个循环将自动展开#pragma unrollfor(int i = 1; i < RADIUS; ++i)tmp += coef[i] * (data[sharedThreadIndex - RADIUS] - data[sharedThreadIndex + RADIUS]);output[globalThreadIndex] = tmp;
}void test(float* input, float* output)
{stencil_1d<<<1, 6>>>(input, output);
}

三、与只读缓存比较

Kepler 系列的GPU允许使用 texture pipeline 作为一个 global Memory 只读缓存。因为这是一个独立的使用单独带宽的只读缓存,所以对带宽限制的kernel性能有很大的提升

每个 Kepler SM 都有48KB的只读缓存。一般来说,只读缓存在分散读取方面比一级缓存更好,当线程束的线程都读取相同地址时,不应使用只读缓存。只读缓存的粒度为32个字节

当通过只读缓存访问全局内存时,需要向编译器指出在内核的持续时间里数据是只读的。有两种方法可以实现这一点:

  • 使用内部函数 __ldg
  • 全局内存的限定指针。内部函数 __ldg 用于替代标准指针解引用,并且强制加载通过只读数据缓存,限定指针为 const   __restrict__,以表明应该通过只读缓存被访问
__global__ void kernel(float* output, float* input) {...output[idx] += __ldg(&input[idx]);...
}
void kernel(float* output, const float* __restrict__ input) {...output[idx] += input[idx];
}

一般使用 __ldg 是更好的选择。通过 constant 缓存存储的数据必须相对较小而且必须获取同一个地址以便获取最佳性能,相反,只读缓存则可以存放较大的数据,且不必地址一致

constant 缓存和只读缓存对于device来说,都是只读的。二者都有大小限制,前者每个 SM 只能有64KB,后者则是48KB。对于读同一个地址,constant 缓存表现好,只读缓存则对地址较分散的情况表现好


http://www.ppmy.cn/news/1570584.html

相关文章

设计模式-生产者消费者模型

阻塞队列&#xff1a; 在介绍生产消费者模型之前&#xff0c;我们先认识一下阻塞队列。 阻塞队列是一种支持阻塞操作的队列&#xff0c;常用于生产者消费者模型&#xff0c;它提供了线程安全的队列操作&#xff0c;并且在队列为空或满时&#xff0c;能够阻塞等待&#xff0c;…

计算机毕业设计Python+Vue.js游戏推荐系统 Steam游戏推荐系统 Django Flask 游 戏可视化 游戏数据分析 游戏大数据 爬虫

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

DeepSeek + IDEA 辅助编程王炸组合

DeepSeek + IDEA 辅助编程王炸组合 2025年的春节可以说是人工智能在中国史上飘红的一段历史时刻,年后上班的第一天,便马不停蹄的尝试新技能。今天的科技在飞速发展,编程领域的人工智能工具犹如雨后春笋般涌现。 其中,DeepSeek 则以其卓越的性能和智能化的功能,迅速在众多…

Spring MVC异常处理:DefaultHandlerExceptionResolver的使用与实例

在开发基于Spring MVC的Web应用程序时&#xff0c;异常处理是一个不可忽视的重要环节。Spring MVC内部在处理请求时可能会抛出多种异常&#xff0c;而这些异常的处理方式直接影响到用户体验和系统的健壮性。今天&#xff0c;我们就来深入探讨一下Spring MVC中默认的异常处理机制…

【蓝桥杯嵌入式】2_LED

全部代码网盘自取 链接&#xff1a;https://pan.baidu.com/s/1PX2NCQxnADxYBQx5CsOgPA?pwd3ii2 提取码&#xff1a;3ii2 1、电路图 74HC573是八位锁存器&#xff0c;当控制端LE脚为高电平时&#xff0c;芯片“导通”&#xff0c;LE为低电平时芯片“截止”即将输出状态“锁存”…

海康威视豆干型网络相机QT的Demo

我用的时候海康官网在arm-linux相关SDK没有给DEMO&#xff0c;只在手册里给了参考代码。自己参考SDK提供的手册作了个QT的DEMO版本。 //main.c #include <QApplication> #include <QWidget> #include <QDebug> #include <QTimer> #include "Hikv…

部署Hadoop高可用集群

注&#xff1a;下述步骤仅供参考&#xff0c;具体指令和操作截图的word版本可见上方本博文免费资源绑定。 1.为虚拟机Hadoop1&#xff0c;2&#xff0c;3拍摄快照以免后续错误操作 2.创建hadoop-HA目录区分之前的集群并将Hadoop安装到该目录下 3.进入/etc目录修改系统环境变量…

深度剖析 Redisson 分布式锁:原理、实现与应用实践

文章目录 写在文章开头详解Redisson 分布式锁使用和实现前置准备工作分布式锁的基本使用公平锁的使用联锁的使用读写锁基本使用常见问题Redisson和Jedis有什么区别redisson如何实现分布式锁redisson如何实现分布式锁的可重入redisson如何实现公平锁Redisson的watchdog机制是什么…