第1章:GPU 基础概念

在写第一行代码之前,我们先理解什么是 GPU,以及为什么它适合做并行计算

本章定位

本章负责建立 GPU kernel 的第一层直觉:为什么 GPU 适合并行、什么是 kernel、thread/block/grid、GPU 内存大概怎么分层。遇到术语不稳时,先查 GPU 初学者术语表

配套主文档:

1.1 什么是 GPU?

生活中的比喻

想象你要计算 1000 道数学题:

CPU 就像一个超级聪明的数学老师

  • 他做题很快
  • 但一次只能做一道题
  • 1000 道题需要一道一道做完

GPU 就像 1000 个小学生

  • 每个学生单独看,做题速度一般
  • 但可以 1000 个学生同时做
  • 1000 道题可以同时完成

这和计算机有什么关系?

CPU(中央处理器):
- 只有几十个核心(比如 8 核、16 核)
- 每个核心很强,能处理复杂任务
- 适合:运行操作系统、打开软件、处理复杂逻辑

GPU(图形处理器):
- 有几千个核心(比如 3072 个、10240 个)
- 每个核心比较简单
- 适合:同时做大量简单计算

为什么叫”图形”处理器?

最初 GPU 是用来渲染游戏画面的。游戏画面由几百万个像素组成,每个像素的颜色需要计算。虽然每个计算很简单,但数量巨大。GPU 就是为了这种任务设计的。

后来人们发现,很多科学计算、AI 计算也是”大量简单计算”,所以 GPU 被广泛应用到这些领域。

1.2 CPU vs GPU 详细对比

架构对比图

CPU 架构(少而精):
┌─────────────────────────────────────┐
│  ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐│
│  │核心 1│ │核心 2│ │核心 3│ │核心 4││
│  │(强大)│ │(强大)│ │(强大)│ │(强大)││
│  └──────┘ └──────┘ └──────┘ └──────┘│
│                                      │
│  ┌─────────────────────────────────┐│
│  │        大容量缓存               ││
│  │      (快速访问常用数据)          ││
│  └─────────────────────────────────┘│
│                                      │
│  ┌─────────────────────────────────┐│
│  │        控制单元(智能)          ││
│  │  分支预测、乱序执行、流水线...   ││
│  └─────────────────────────────────┘│
└─────────────────────────────────────┘

GPU 架构(多而简):
┌─────────────────────────────────────┐
│ 核心核心核心核心核心核心核心核心核心 │
│ 核心核心核心核心核心核心核心核心核心 │
│ 核心核心核心核心核心核心核心核心核心 │
│ ...成千上万个简单核心...            │
│                                      │
│ 小容量缓存(每个核心组共享)        │
│                                      │
│ 简单控制单元(大家做同样的事)      │
└─────────────────────────────────────┘

性能指标对比

指标CPUGPU说明
核心数4-64 个1000-10000+ 个GPU 核心数量多得多
单核性能CPU 单核更强
总计算能力中等极高并行时 GPU 更强
内存带宽~100 GB/s~1000 GB/sGPU 内存更快
时钟频率3-5 GHz1-2 GHzCPU 频率更高
适合任务复杂逻辑并行计算各有所长

1.3 什么是并行计算?

串行 vs 并行

串行计算(CPU 擅长)

任务1 → 任务2 → 任务3 → 任务4 → 完成
时间: 1秒   1秒    1秒    1秒    总共4秒

并行计算(GPU 擅长)

任务1 ┐
任务2 │→ 同时执行 → 完成
任务3 │             总共1秒
任务4 ┘

什么样的任务适合 GPU?

适合 GPU 的任务特点

  1. 数据量大:需要处理很多数据
  2. 计算简单:每个数据的计算不复杂
  3. 相互独立:数据之间不需要等待

例子:图像处理

  • 一张 4K 图片有 830 万个像素
  • 每个像素需要做同样的处理(比如变亮)
  • 每个像素的处理很简单
  • 像素之间相互独立
  • → 非常适合 GPU!

例子:大模型推理

  • GPT 生成的每个 token 需要大量矩阵计算
  • 矩阵乘法就是很多简单的加法和乘法
  • → 非常适合 GPU!

不适合 GPU 的任务

  1. 复杂的逻辑判断(很多 if-else)
  2. 需要大量数据共享和同步
  3. 数据量很小

1.4 GPU 编程的基本概念

什么是 Kernel?

Kernel = 在 GPU 上运行的函数

普通函数:在 CPU 上运行
Kernel:在 GPU 上运行,可以被成千上万个线程同时执行

一个简单的例子

假设我们要把一个数组的每个元素都乘以 2:

CPU 方式(串行)

// 在 CPU 上一个一个处理
void multiply_cpu(float* data, int n) {
    for (int i = 0; i < n; i++) {
        data[i] = data[i] * 2;  // 一次处理一个
    }
}
// 100万个元素,需要循环100万次

GPU 方式(并行)

// 在 GPU 上,100万个线程同时执行
__global__ void multiply_gpu(float* data) {
    int i = threadIdx.x;  // 每个线程知道自己处理哪个元素
    data[i] = data[i] * 2;  // 每个线程处理一个元素
}
// 启动100万个线程,同时执行!
multiply_gpu<<<1, 1000000>>>(data);

线程、线程块、网格

这是 CUDA 编程最重要的概念之一:

Grid(网格)
│   整个 GPU 计算任务
│
├── Block 0(线程块)
│   ├── Thread 0
│   ├── Thread 1
│   ├── Thread 2
│   └── ...(最多1024个线程)
│
├── Block 1(线程块)
│   ├── Thread 0
│   ├── Thread 1
│   └── ...
│
└── Block N(线程块)
    └── ...

比喻

  • Grid = 一个工厂
  • Block = 一个车间
  • Thread = 一个工人

每个工人(线程)做同样的事,但处理不同的数据。

为什么需要线程块?

你可能会问:为什么不直接用几万个线程?

原因:

  1. 硬件限制:每个 Block 最多 1024 个线程
  2. 协作需要:同一 Block 内的线程可以共享内存、同步
  3. 灵活调度:GPU 可以把不同的 Block 分配给不同的处理器

1.5 GPU 内存基础

GPU 内存的层次

┌─────────────────────────────────────┐
│        全局内存 (Global Memory)      │ ← 最大,最慢
│           显存 (VRAM)               │   几十GB
│         带宽: 1-2 TB/s             │
├─────────────────────────────────────┤
│        共享内存 (Shared Memory)     │ ← 快10-20倍
│          用户可管理                 │   每个Block ~100KB
│         带宽: ~20 TB/s             │
├─────────────────────────────────────┤
│        寄存器 (Registers)           │ ← 最快
│          每个-thread 独立           │   每个线程 ~255个
│         带宽: 最高                  │
└─────────────────────────────────────┘

内存访问速度对比

内存类型延迟(时钟周期)比喻
寄存器1从口袋里拿东西
共享内存~20从桌上拿东西
全局内存~400从另一个房间拿东西

优化原则:尽量使用更快、更近的内存。

1.6 什么是 CUDA?

CUDA 简介

CUDA = NVIDIA 开发的 GPU 编程平台

  • 全称:Compute Unified Device Architecture
  • 只能在 NVIDIA 的 GPU 上使用
  • 使用 C/C++ 语法,加上一些特殊关键字

CUDA 程序的基本结构

// 1. 在 GPU 上运行的函数(Kernel)
__global__ void my_kernel(float* data) {
    int idx = threadIdx.x;
    data[idx] = data[idx] * 2;
}
 
// 2. 主程序在 CPU 上运行
int main() {
    // 分配 CPU 内存
    float* h_data = (float*)malloc(1000 * sizeof(float));
    
    // 分配 GPU 内存
    float* d_data;
    cudaMalloc(&d_data, 1000 * sizeof(float));
    
    // 把数据从 CPU 拷贝到 GPU
    cudaMemcpy(d_data, h_data, 1000 * sizeof(float), cudaMemcpyHostToDevice);
    
    // 启动 Kernel(1000个线程)
    my_kernel<<<1, 1000>>>(d_data);
    
    // 把结果从 GPU 拷贝回 CPU
    cudaMemcpy(h_data, d_data, 1000 * sizeof(float), cudaMemcpyDeviceToHost);
    
    // 释放内存
    cudaFree(d_data);
    free(h_data);
    
    return 0;
}

关键字解释

关键字含义例子
__global__定义 Kernel 函数__global__ void my_kernel()
__device__在 GPU 上运行的普通函数__device__ float add(float a, float b)
__host__在 CPU 上运行(默认)__host__ void cpu_func()
threadIdx.x当前线程在 Block 内的索引第3个线程,threadIdx.x = 2
blockIdx.x当前 Block 在 Grid 内的索引第2个 Block,blockIdx.x = 1
blockDim.xBlock 内线程数量如果每个 Block 256个线程,blockDim.x = 256

1.7 学习路线预览

学完本章后,你已经理解了:

  • ✅ GPU 是什么,为什么适合并行计算
  • ✅ CPU 和 GPU 的区别
  • ✅ 什么是 Kernel、线程、线程块
  • ✅ GPU 内存的层次结构
  • ✅ CUDA 程序的基本结构

接下来,我们将在 第2章 中:

  • 搭建开发环境
  • 编写第一个 CUDA 程序
  • 深入理解线程模型

💡 本章要点总结

  1. GPU 适合并行计算:大量简单的任务同时执行
  2. Kernel 是 GPU 函数:可以被成千上万个线程同时执行
  3. 线程组织成 Block,Block 组织成 Grid
  4. 内存有层次:寄存器最快,全局内存最慢
  5. CUDA 是 NVIDIA 的 GPU 编程平台

❓ 常见问题

Q:我需要买 NVIDIA 显卡吗? A:如果只是学习,可以用 Google Colab 的免费 GPU。如果要认真做开发,建议买一张 RTX 3060 或更高。

Q:AMD 显卡能用 CUDA 吗? A:不能。CUDA 只支持 NVIDIA 显卡。AMD 有自己的 ROCm 平台,但教程和生态不如 CUDA 完善。

Q:我需要学多少数学? A:基础的线性代数(矩阵乘法)就够用。深入学习时可能需要更多数学知识。

📝 课后练习

  1. 用自己的话解释 CPU 和 GPU 的区别(不要用专业术语)
  2. 想想生活中有哪些任务是”串行”的,哪些是”并行”的
  3. 为什么 GPU 不能完全取代 CPU?
  4. 如果有 100 万个数字要相加,用 GPU 怎么做?(只需要描述思路)

下一章:CUDA 入门 →