当前位置: 首页 > news >正文

cuda编程笔记(34)-- 内存访问控制与缓存提示

在cuda编程笔记(23)-- __threadfence()和__ldg()-CSDN博客,提到了__ldg,本文将cuda里内存访问的相关api都介绍一下

__ldg

功能:从 global memory 通过只读缓存(read-only cache)加载数据。

template <typename T>
__device__ __forceinline__ T __ldg(const T* ptr);

注意这并不是函数原型,但是你可以这么理解,T并不是所有类型都能适配的。

  • 在老架构(Maxwell/Kepler)上,它会使用 LDG 指令,利用只读缓存(texture cache / L1 read-only)。

  • 在现代架构(Pascal+)仍然有效,但新架构 L2 的自动缓存更智能。

  • 应用场景:当你的 global memory 数据只读且多线程共享,使用 __ldg 可以减少对 L1 的压力,提高带宽利用。

__global__ void kernel(const float* data, float* out) {int idx = threadIdx.x + blockIdx.x * blockDim.x;float val = __ldg(&data[idx]);out[idx] = val * 2.0f;
}
架构代号Compute CapabilityRead-Only Cache 状态__ldg() 行为
Kepler (GK110)3.5✅ 独立的只读缓存有效,大幅优化
Maxwell / Pascal5.x / 6.x✅ 仍保留,只读缓存共享与 L1有效,但作用减弱
Volta / Turing / Ampere7.x / 8.x⚠️ 与 L1 Cache 合并__ldg() 存在,但与普通 *ptr 效果几乎一样
Hopper (H100)9.0❌ 不再单独实现__ldg() 仍编译,但仅作普通 load

Ampere 及以后

  • Read-only cache 不再是独立单元;

  • 所有 load 都经过一个统一的 L1 Cache(Unified Data Cache)

  • __ldg() 仍然存在,编译器不会报错;

  • 但 PTX 层会退化为普通的 LDG.ELDG.G 指令,与 *ptr 无区别。

__prefetch_global

功能:提前将 global memory 数据加载到 cache,隐藏访问延迟。

template <typename T>
__device__ void __prefetch_global(const T* ptr);

说明

  • Ampere 架构开始支持。

  • 只是 hint,不保证立即访问到数据,只是告诉硬件“接下来可能用到”。

  • 对延迟敏感的循环访问场景非常有用。

__global__ void kernel(float* data) {int idx = threadIdx.x + blockIdx.x * blockDim.x;__prefetch_global(&data[idx]);float val = data[idx];  // 实际访问时可能已经在 L1/L2
}

但是普通的游戏显卡,比如3090,4090可能不支持

架构GPU类型SM版本是否支持 __prefetch_global()说明
A100数据中心sm_80✅ 支持pipeline copy / prefetch 全支持
H100数据中心sm_90✅ 支持支持更多 pipeline 指令
RTX 3090 / 3080消费级sm_86❌ 不支持指令未开放,prefetch 会报错
RTX 4090 (Ada)消费级sm_89❌ 不支持同样未公开 prefetch API

__ldcs() / __ldcg() / __ldca()

功能:控制加载时的缓存策略。

API含义
__ldcs()load via streaming cache(只 L2)
__ldcg()load global memory (cached)
__ldca()load via L1 cache
template <typename T>
__device__ T __ldcs(const T* ptr);
template <typename T>
__device__ T __ldcg(const T* ptr);
template <typename T>
__device__ T __ldca(const T* ptr);

说明

  • Ampere/Hopper 架构提供,允许程序员控制 L1/L2 的命中策略。

  • 对优化多线程数据共享、减少 L1 冲突有帮助。

  • 对大多数通用算法不是必需,更多是高性能调优手段。

__stcs() / __stcg() / __stwb()

功能:控制 store(写)时缓存策略。

API含义
__stcs()streaming store (L2 only)
__stcg()cached store
__stwb()write-back store

说明

  • 可以降低 L1 负担或者控制写合并策略。

  • 对性能优化高并发写场景有时有效。

__pipeline_memcpy_async

功能:异步拷贝数据到 shared memory / register / L2 pipeline。

  • 原型(示意)

template <typename T>
__device__ void __pipeline_memcpy_async(T* dst, const T* src, size_t n);

说明

  • Ampere+ GPU 支持。

  • 类似 cp.async 指令,但 API 级别,更安全。

  • 允许 overlap memory copy 与 compute,隐藏 global memory 访问延迟。

  • 必须配合 __pipeline_commit()__pipeline_wait_prior() 控制阶段。

__pipeline_commit() / __pipeline_wait_prior()

功能:控制 pipeline 的异步拷贝阶段。

  • 用法

    • __pipeline_commit():提交之前的 async copy 指令到 pipeline。

    • __pipeline_wait_prior():等待 pipeline 中之前提交的 copy 完成。

  • 说明

    • 通常配合循环或双缓冲使用,实现 compute 与 copy 的 overlap。

    • 相当于 Ampere+ 的“软件可控 cp.async”。

for(int i=0;i<N;i+=tile){__pipeline_memcpy_async(sm_tile, &gmem[i], tile);__pipeline_commit();__pipeline_wait_prior();  // 等待上一次拷贝完成compute(sm_tile);
}

GPU缓存结构

CUDA GPU 的存储层级(从慢到快)大致是:

Global Memory (DRAM)│▼L2 Cache  ← 所有 SM 共享│┌──────┴──────────┐│                 │▼                 ▼
L1 Data Cache   Read-Only Cache (texture)│▼
Registers / Shared Memory
  • Global Memory (DRAM):显存,全局可访问,但延迟高(几百个 cycles)。

  • L2 Cache:芯片级共享缓存,所有 SM 都能访问,延迟较低(几十个 cycles)。

  • L1 Cache:每个 SM 独立的一级缓存,延迟更低(10-20 cycles)。

  • Read-Only Cache:专门优化只读访问(只在老架构中单独存在,现在常与 L1 合并或共享)。

  • Registers / Shared Memory:线程或线程块级的高速存储,延迟极低。

我们来对照看下不同指令的缓存行为。

指令 / API访问路径缓存层级特点典型用途
__ldgGlobal → Read-only cache → RegisterL2 + Read-only(L1T)只读缓存,不污染普通 L1共享常量、查表数据
__ldcgGlobal → L2 → L1 → RegL2 + L1默认路径(全缓存)一般数据加载
__ldcaGlobal → L1 only → RegL1 only(不走 L2)强制优先用 L1,L2可不命中临时数据,多次局部访问
__ldcsGlobal → L2 only → RegL2 only(跳过 L1)不污染 L1 cache大流量读取(streaming read)

但实际上,这些全是“建议式控制”,不是显式管理。

L1/L2/只读缓存的对比总结

缓存层作用范围容量一致性可写典型用途
L1 Data Cache每个 SM 独立小(128KB~192KB)不全局一致可写局部数据缓存
Read-only Cache每个 SM 独立小(48KB~128KB)无需一致性只读常量、查表
L2 Cache全 SM 共享大(几 MB)全局一致可写跨 SM 通信、共享数据

实际调优经验

场景推荐策略理由
常量查表 / 只读共享__ldg走 read-only cache,不污染 L1
大数据流式读__ldcs避免 L1 被大量数据污染
局部复用强(tile/block 内)__ldca强制走 L1,本地复用效率高
一般数据访问默认 / __ldcg默认行为即可
Ampere+ 异步拷贝__pipeline_memcpy_async取代手动 prefetch,性能更高

SM 与缓存的关系

SM(Streaming Multiprocessor) 是 GPU 的计算核心单元,就像 CPU 里的“核(core)”。
一张 GPU(比如 A100、RTX 4090)内部通常有 几十到上百个 SM

CUDA 的层级关系:

Grid  →  Block  →  Warp  →  Thread

SM 是执行的硬件实体:

概念含义对应的硬件
Grid一次 kernel 启动的所有线程集合GPU 整体
Block一组线程(可通信/同步)分配给一个 SM 执行
Warp32 个线程组成的调度单元SM 内的调度器发射
Thread单个执行流SM 内的计算核心(ALU)

每个 block 在运行时都会被分配到某个 SM 上执行,
block 内的所有线程都在那个 SM 的共享资源(如 shared memory、L1 cache)中运行。

每个 SM 有自己独立的 L1 Cache 和 Shared Memory,所以:

  • 一个 SM 里的线程块(block)可以在 shared memory 中高速共享数据;

  • 不同 SM 之间的数据共享必须通过 L2 CacheGlobal Memory

  • L1 cache 不同步(每个 SM 自己的 L1 可能不一致);

  • L2 cache 在所有 SM 间是 一致的(coherent)

这也就是为什么有下面这种访问策略:

缓存类型是否 SM 共享一致性示例 API
L1 Cache❌ 独立不一致__ldca()
Read-only Cache❌ 独立不一致__ldg()
L2 Cache✅ 所有 SM 共享一致__ldcs()
Global Memory✅ 所有 SM 共享一致

SM的小知识

可以用下面的代码查看自己 GPU 的 SM 数量:

cudaDeviceProp prop;
cudaGetDeviceProperties(&prop, 0);
std::cout << "SM数量: " << prop.multiProcessorCount << std::endl;

比如3090输出82

“核函数一次最多启动的 block 数不能超过 SM 数”

这是 不对的
block 数可以远远多于 SM 数,只不过 同时在 GPU 上运行的 block 数受到 SM 数量和资源限制

正确理解

核函数启动时你写的:

myKernel<<<numBlocks, threadsPerBlock>>>(...);

里指定的 numBlocks整个 Grid 的大小
GPU 会把这些 block 分批调度到 SM 上执行。

举个具体例子(比如 A100):

  • 每个 SM 最多同时运行 2048 线程

  • 每个 block 有 256 线程

  • 那么每个 SM 同时最多能运行 8 个 block

  • 若总共有 108 个 SM,则同时最多有 108 × 8 = 864 个 block 在执行
    但如果你启动了 10,000 个 block,其他的会排队等前面的执行完再上。

层次存储位置是否共享说明
寄存器 (Registers)每个线程私有每个线程自己的最快速存储空间(延迟 < 10 cycles)。编译器自动分配。
共享内存 (Shared Memory)每个 Block 独占✅(同 Block 内共享)位于 SM 内部的片上 SRAM。延迟 ~100 cycles,比 global memory 快很多。
L1 Cache每个 SM 独占✅(同 SM 上的所有 Block 共享)用于缓存 global memory 加载的结果,可配置与 Shared Memory 共用物理空间。
L2 CacheGPU 所有 SM 共享✅(全局共享)位于芯片片上(on-chip),负责不同 SM 之间的数据共享与一致性。
Global Memory (DRAM)芯片外显存✅(全局共享)访问延迟几百到上千 cycles。
http://www.dtcms.com/a/536356.html

相关文章:

  • webserver类续
  • 条款22:使用Pimpl惯用法时,将特种成员函数的定义放到实现文件中
  • 基于python大数据的省级城市政企客户业务分析系统
  • 合肥微网站制作初中做语文综合题的网站
  • dedecms做论坛网站广州网页设计师学校
  • Blender + MCP 全流程详细图文教程
  • 自定义classload实现热加载案例
  • 上海网站建设seo站霸网络网站建设推销拜访客户怎么开头
  • Spring Bean的生命周期 第二次思考
  • HttpServletResponse下载文件
  • vue3的路由详解
  • Spring Boot 生产就绪特性浅析(一)
  • 如何做彩票网站信息手机上打开html的软件
  • 【图像处理基石】图像对比度增强入门:从概念到实战(Python+OpenCV)
  • 网站建设公司六安全国连锁装修公司加盟
  • 直播互动小程序端Web运营端接入指南
  • Java—抽象类
  • 坛墨网站建设wordpress 邀请
  • idc网站模版百度提交
  • 树莓派3B+降低功耗及恢复脚本
  • 开源项目解读4-高性能并发缓存库Ristretto
  • 《微信小程序》第五章:登录-API封装
  • MYSQL数据库入门操作
  • 青岛建设集团网站101工业设计有限公司
  • wordpress 网站上传到服务器错误怎么建设手机网站
  • MySQL 下载、安装及配置教程(Msi安装)
  • AWS CloudTrail 可观测最佳实践
  • 商城网站设计公司十大奢侈品排名
  • 建设部网站从哪登陆网站建设学什么书
  • STM32学习(MCU控制)(NVIC)