news 2026/10/27 1:57:24

3. CUDA 执行模型

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
3. CUDA 执行模型

1. CUDA 执行模型解决什么问题?

CUDA 编程模型告诉我们:

如何使用 Thread、Block 和 Grid 描述一个并行任务。

CUDA 执行模型进一步回答:

GPU 如何把这些线程组织起来,并实际执行它们?

一次 Kernel 启动后,GPU 大致会经历以下过程:

Grid ↓ Block 被分配到 SM ↓ Block 中的 Thread 被划分为 Warp ↓ Warp 以 SIMT 方式执行 ↓ 线程访问寄存器、共享内存或全局内存

2. Thread、Block 和 Grid 的实际含义

2.1 Thread:逻辑执行单位

Thread 是 CUDA 中最小的逻辑执行单位。

例如,计算两个数组相加:

C[i] = A[i] + B[i]

可以让每个线程负责一个元素:

Thread 0:计算 C[0] Thread 1:计算 C[1] Thread 2:计算 C[2]

每个线程通常拥有自己的:

  • 线程索引
  • 寄存器
  • 局部变量
  • 执行状态

不过,Thread 是程序员看到的逻辑概念,并不等于 GPU 中一个独立的物理核心。

2.2 Block:线程协作和调度单位

Block 是一组线程的集合。

例如:

Block 0 ├─ Thread 0 ├─ Thread 1 ├─ Thread 2 └─ ...

Block 有两个重要作用:

  1. 作为 GPU 调度的基本单位
  2. 允许其中的线程进行协作和同步

同一个 Block 中的线程可以通过共享内存交换数据,并使用线程同步机制协调执行。

需要注意:

Block 是调度单位,但不是一个固定的硬件核心。

一个 Block 可能包含多个 Warp,并由一个 SM 负责执行。

2.3 Grid:一次 Kernel 启动的全部线程块

一次 Kernel 启动产生的所有 Block 组成一个 Grid:

Grid ├─ Block 0 ├─ Block 1 ├─ Block 2 └─ Block 3

Grid 只表示一次 Kernel 启动包含的全部计算任务,不表示这些 Block 会同时执行。

实际运行时:

  • GPU 可能同时执行部分 Block
  • 其他 Block 等待资源空闲后再执行
  • Block 的执行顺序通常不应由程序假设

3. SM 与线程块调度

3.1 SM 是什么?

SM 是Streaming Multiprocessor的缩写,可以理解为 GPU 中负责执行线程块的处理单元。

一个 SM 通常包含:

  • 用于执行指令的计算资源
  • 寄存器
  • 共享内存
  • Warp 调度相关资源

GPU 中通常有多个 SM:

GPU ├─ SM 0 ├─ SM 1 ├─ SM 2 └─ ...

3.2 Block 如何分配到 SM?

当 Kernel 启动后,GPU 会把 Grid 中的 Block 分配给可用的 SM:

Grid ├─ Block 0 ──> SM 0 ├─ Block 1 ──> SM 1 ├─ Block 2 ──> SM 0 └─ Block 3 ──> 等待资源

一个 SM 可以同时驻留多个 Block,但能驻留多少 Block,取决于资源限制,例如:

  • SM 中可用的寄存器数量
  • 共享内存使用量
  • 线程数量限制
  • Block 数量限制

因此,Block 不一定会立即执行,也不一定全部同时执行。

3.3 Block 为什么应该相互独立?

CUDA 通常按照“Block 之间相互独立”的方式设计程序。

原因是:

  • 不同 Block 可能被分配到不同 SM
  • 不同 Block 的执行速度可能不同
  • 不同 Block 的执行顺序通常不固定
  • 某些 Block 可能要等资源释放后才能启动

因此,下面这种假设是不安全的:

Block 0 一定先于 Block 1 执行 Block 0 执行完后 Block 1 才开始 所有 Block 一定同时执行

更可靠的设计方式是:

让每个 Block 尽量独立完成自己的工作。

如果不同阶段之间存在严格依赖,通常可以拆分为多个 Kernel:

Kernel 1 完成 ↓ Kernel 2 开始

4. Warp 与 SIMT 执行

4.1 Warp 是什么?

Warp 是 GPU 实际调度线程的重要执行单位。

在 NVIDIA CUDA 中:

一个 Warp 通常包含 32 个线程。

例如,一个 Block 有 128 个线程,通常会被划分为:

Warp 0:Thread 0 ~ Thread 31 Warp 1:Thread 32 ~ Thread 63 Warp 2:Thread 64 ~ Thread 95 Warp 3:Thread 96 ~ Thread 127

Block 是调度单位,而 Warp 是 SM 中实际进行指令调度的重要单位。

4.2 什么是 SIMT?

SIMT 是Single Instruction, Multiple Threads的缩写,意思是:

单条指令同时作用于多个线程。

例如,下面的 Kernel:

__global__voidadd(int*data){inti=/* 当前线程索引 */;data[i]=data[i]+1;}

当一个 Warp 中的线程执行这段代码时,通常会执行相同的加法指令,但每个线程使用自己的i,因此操作不同的数据。

可以理解为:

同一条指令: data[i] = data[i] + 1 Thread 0 使用 i = 0 Thread 1 使用 i = 1 Thread 2 使用 i = 2 ...

这就是“相同指令,不同线程,不同数据”。

4.3 Warp 中的线程不一定完全独立

虽然程序员把每个 Thread 看成独立执行,但硬件通常以 Warp 为单位调度。

如果一个 Warp 中的线程走不同分支:

if(condition){// 路径 A}else{// 路径 B}

而同一个 Warp 中:

部分线程满足 condition 部分线程不满足 condition

就会出现分支分化。

4.4 什么是分支分化?

分支分化是指同一个 Warp 中的线程需要执行不同的分支路径。

GPU 通常需要分阶段执行这些路径:

先执行路径 A - 满足条件的线程有效 - 其他线程暂时等待 再执行路径 B - 不满足条件的线程有效 - 其他线程暂时等待

因此,虽然线程逻辑上走了不同路径,但硬件可能需要依次执行这些路径。

示例:

if(threadIdx.x%2==0){doA();}else{doB();}

同一个 Warp 中通常既有偶数线程,也有奇数线程,因此可能发生分支分化。

需要注意:

分支分化不一定导致程序错误,但可能降低执行效率。

5. 一个完整的执行示例

假设启动一个 Kernel:

kernel<<<2,64>>>();

它表示:

  • Grid 中有 2 个 Block
  • 每个 Block 有 64 个 Thread
  • 总线程数为 128

执行过程可以理解为:

Grid ├─ Block 0:64 个线程 │ ├─ Warp 0:32 个线程 │ └─ Warp 1:32 个线程 │ └─ Block 1:64 个线程 ├─ Warp 2:32 个线程 └─ Warp 3:32 个线程

随后:

  1. GPU 将 Block 分配给可用的 SM
  2. 每个 Block 中的线程被划分成 Warp
  3. SM 调度 Warp 执行指令
  4. Warp 中的线程以 SIMT 方式执行
  5. 每个线程根据自己的索引访问对应数据

需要注意:

  • Block 0 不一定先于 Block 1 执行
  • Warp 0 不一定一直连续执行到结束
  • GPU 可能在多个 Warp 之间切换
  • 程序不应依赖这些未保证的顺序

6. 线程执行与内存访问

线程执行不仅包括计算指令,也包括访问数据。

CUDA 中常见的内存层次如下:

内存类型主要使用者特点
寄存器单个线程速度快,每个线程独有
共享内存同一个 Block 的线程速度较快,可用于线程协作
全局内存Grid 中的所有线程容量较大,但访问延迟较高
常量内存等只读空间多个线程适合读取不会改变的数据

6.1 寄存器

每个线程通常有自己的寄存器,用于保存:

  • 临时变量
  • 计算中间结果
  • 线程索引

一个线程不能直接使用另一个线程的寄存器。

6.2 共享内存

同一个 Block 中的线程可以使用共享内存交换数据。

例如:

Block 内的多个线程 ↓ 共同使用共享内存

共享内存适合用于:

  • 缓存重复访问的数据
  • 线程之间交换中间结果
  • 减少对全局内存的访问

但使用共享内存时,通常需要考虑线程同步,避免一个线程读取到尚未准备好的数据。

6.3 全局内存

全局内存可以被多个 Block 访问,容量通常较大,但访问延迟也较高。

如果一个 Warp 中的线程访问相邻地址,通常更有利于内存访问效率。相反,如果线程访问地址非常分散,可能降低效率。

本次只需记住:

GPU 程序的执行效率不仅取决于计算,还取决于线程如何访问内存。

7. 同步与执行边界

7.1 Block 内同步

同一个 Block 中的线程可以使用同步机制:

__syncthreads();

它的作用是:

让同一个 Block 中的线程在某个位置等待,直到该 Block 中满足条件的线程都到达这里。

典型场景:

线程将数据写入共享内存 ↓ 等待其他线程写入完成 ↓ 线程读取共享内存

如果没有正确同步,可能出现:

线程 A 还没有写完数据 线程 B 已经开始读取数据

从而导致结果错误。

7.2 Block 之间不能随意同步

普通 CUDA Kernel 中,不同 Block 之间通常不能直接使用普通的 Block 内同步机制。

原因是:

  • 不同 Block 可能运行在不同 SM
  • 不同 Block 的执行时间不同
  • 某些 Block 可能还没有开始执行
  • 如果互相等待,可能造成死锁

因此,跨 Block 的阶段依赖通常使用多个 Kernel 表示:

Kernel 1: 不同 Block 完成第一阶段 ↓ Kernel 1 结束 ↓ Kernel 2: 不同 Block 开始第二阶段

7.3 Kernel 是重要的执行边界

不同 Kernel 之间通常形成明确的阶段边界:

Kernel 1 ↓ Kernel 2

如果 Kernel 2 依赖 Kernel 1 的结果,就应确保 Kernel 1 已经完成,或者使用适当的 CUDA 同步机制。

所以,Kernel 不只是一个函数,也是:

  • 一个 GPU 任务阶段
  • 一个全局依赖边界
  • 一个程序组织并行计算的基本单位

8. 常见误解

误解一:一个线程对应一个物理核心

不是。

Thread 是逻辑执行单位。GPU 通常以 Warp 为单位调度线程,实际硬件资源会被多个线程和 Warp 共同使用。

误解二:一个 Block 就是一个 Warp

不一定。

Block 可以包含多个 Warp。例如:

Block 有 256 个线程 256 ÷ 32 = 8 个 Warp

误解三:所有线程都严格同时执行

不完全是。

一个 Warp 中的线程通常执行相同指令,但不同 Warp 之间可能交替执行,Block 之间也可能按不同顺序执行。

误解四:Block 一定按照编号顺序执行

不是。

Block 的执行顺序通常不应由程序假设。

误解五:__syncthreads()可以同步整个 Grid

不是。

它主要用于同步同一个 Block 中的线程,不能直接作为普通的 Grid 级同步机制。

误解六:分支分化会让程序一定出错

不一定。

分支分化通常首先影响性能。只有在程序错误地依赖线程执行顺序或同步关系时,才可能导致结果错误。

9. 编程模型与执行模型的联系

两者可以按照下面的方式理解:

层次关注内容
编程模型程序员如何组织 Host、Device、Kernel、Grid、Block、Thread
执行模型GPU 如何将 Block 分配到 SM,如何划分 Warp 并执行
硬件实现SM 中的计算单元、寄存器、缓存和调度资源

整体关系如下:

程序员编写 Kernel ↓ 使用 Grid、Block、Thread 描述任务 ↓ GPU 将 Block 分配到 SM ↓ Thread 被划分为 Warp ↓ Warp 按 SIMT 方式执行 ↓ 线程访问不同层次的内存

编程模型是程序员使用的抽象,执行模型是 GPU 实际运行这些抽象的方式。

10. 总结

CUDA 执行模型的核心可以概括为:

Grid 中的 Block 被分配到 SM,Block 中的 Thread 被划分为 Warp,Warp 以 SIMT 方式执行指令。

需要掌握的关键关系是:

Grid:一次 Kernel 启动的全部 Block Block:GPU 调度和线程协作的基本单位 Thread:最小逻辑执行单位 Warp:通常包含 32 个线程,是重要的硬件调度单位 SM:负责执行一个或多个 Block 的 GPU 处理单元 SIMT:多个线程执行相同指令,但处理各自的数据

还需要记住三个边界:

  1. 不要假设 Block 的执行顺序
  2. 不要把普通 Thread 等同于物理核心
  3. 不要把 Block 内同步机制当作整个 Grid 的同步机制
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/27 1:57:20

基于CCT的传统普朗克光源插值方法 —— 算法原理详解

CCT-Based Interpolation Methods for Traditional Planckian Illuminants 基于CCT的传统普朗克光源插值方法 —— 算法原理详解 整理自论文 S5 节 “CCT-Based Interpolation Methods for Traditional Planckian Illuminants”、S5.1 “Unified CCT Estimation Algorithm”、…

作者头像 李华
网站建设 2026/10/27 1:55:35

8. 复合语句

8. 复合语句 复合语句指的是那种包含了其他语句, 也可以说是语句组在内的语句&#xff1b;它的主要作用是以某种特定的方式来影响或者去控制在内部所包裹的那些其他语句究竟是怎样去执行下去的。 在通常的一般情况之下, 这种复合语句往往会跨越好几行才能展示完整, 虽然在有的…

作者头像 李华
网站建设 2026/10/27 1:55:13

ORB-SLAM3 void Tracking::SearchLocalPoints()

该函数是 TrackLocalMap() 中的关键一步:在局部地图构建完成后,将局部地图点投影到当前帧,搜索 ORB 匹配,为后续位姿优化提供更多观测。 void Tracking::SearchLocalPoints() {// Do not search map points already matchedfor(vector<MapPoint*>::iterator vit=mCu…

作者头像 李华
网站建设 2026/10/27 1:54:52

COBRA‑Skills 论文总结与翻译

# COBRA‑Skills 论文总结与翻译> 论文信息:arXiv:2609.11682,COBRA‑Skills: Contextual Bandit‑Guided Evolution for Agent Skill Optimization ## 一、论文主要内容本文针对大语言模型Agent的可复用技能优化问题展开研究。现有Agent技能优化方法高度依赖开销巨大的执…

作者头像 李华
网站建设 2026/10/27 1:54:38

Just‑in‑Time Memory 论文阅读笔记

# Just‑in‑Time Memory 论文阅读笔记> 论文地址:https://arxiv.org/abs/2609.27334> 标题:*Just‑in‑Time Memory: Learning to Curate Task‑Adaptive Memory for LLM Agents*> 作者:Yefan Zhou, Yang Li, Zeyu Leo Liu, Semih Yavuz, Shafiq Joty> 领域:大…

作者头像 李华