并发与并行:概念及对应关系
1. 并行(Parallelism)
GPU
概念: 并行是指物理意义上的同时执行,即多个任务在硬件上真正同时运行。在 CUDA 中,这通常依赖于 GPU 的多核架构,允许大量线程或任务并行执行。
在 CUDA 编程模型中的体现:
- 线程级并行:
CUDA 内核函数中的每个线程是独立执行的,且同一线程块内的线程可通过共享内存协作。GPU 的 流式多处理器(SM) 可以同时调度多个线程块或线程束(warp)并行执行。
- 线程块(Block):每个线程块被分配到一个 SM 上执行,块内的线程并行运行。
- 线程束(Warp):32 个线程组成一个 warp,SM 按 warp 粒度调度执行(SIMD 模式)。
- 任务级并行:
不同的 CUDA 操作(如内核启动、内存拷贝)在硬件上可并行执行。例如,GPU 支持同时运行多个内核(
kernel<<<...>>>)或同时进行内存传输(cudaMemcpyAsync)。
在硬件模型中的体现:
- GPU 架构: NVIDIA GPU 包含数百至数千个 CUDA 核心,每个 SM 可同时处理多个线程块。例如,NVIDIA Ampere 架构的 SM 支持最多 32 个线程块(通过 动态并行 和 硬件资源分配)。
- 内存层次: 全局内存、共享内存和寄存器的协同设计,使得线程间数据访问高效,支持大规模并行计算。
CPU
概念: 并行是指多个任务在同一时刻真正同时执行,依赖硬件的多核或多处理器架构。每个任务在独立的硬件单元(如 CPU 核心)上运行,物理上完全独立,无需依赖时间片切换。
在硬件中的体现:
- 多核 CPU:每个核心独立执行指令,拥有独立的寄存器、缓存和执行单元。
- 分布式系统:多台计算机通过网络协作,任务分解到不同节点并行执行。
在软件中的体现:
- 操作系统调度器:将任务分配到不同的 CPU 核心,利用多核并行执行。
- 并行编程模型:如 OpenMP(共享内存并行)、MPI(分布式内存并行),显式划分任务到不同核心/节点。
示例:
- 科学计算:矩阵乘法分解到多个核心并行计算。
- 多线程程序:在多核 CPU 上,多个线程分别运行在不同核心上。
2. 并发(Concurrency)
GPU
概念: 并发是指逻辑意义上的交替执行,即多个任务通过时间片轮转或资源复用的方式看似同时运行。在 CUDA 中,这通常通过 CUDA Stream 实现,允许不同任务在逻辑上交错执行,但物理上可能共享硬件资源。
在 CUDA 编程模型中的体现:
-
CUDA Stream: CUDA Stream 是任务队列,用于管理 GPU 上异步操作(如内核启动、内存拷贝)。
- 默认流(Stream 0):所有未显式指定流的操作会按顺序执行(隐式同步)。
- 非默认流:通过创建多个流(
cudaStreamCreate),不同流中的操作可以并发执行(需满足硬件条件)。
示例:
cudaStream_t stream1, stream2; cudaMemcpyAsync(d_data1, h_data1, size, cudaMemcpyHostToDevice, stream1); // 流1中的内存拷贝 kernel1<<<grid, block, 0, stream1>>>(d_data1); // 流1中的内核 cudaMemcpyAsync(d_data2, h_data2, size, cudaMemcpyHostToDevice, stream2); // 流2中的内存拷贝 kernel2<<<grid, block, 0, stream2>>>(d_data2); // 流2中的内核在硬件支持下,
kernel1和kernel2可能并行执行,或与内存拷贝操作重叠。 -
并发与并行的结合:
- 计算与内存传输的重叠:
通过不同流的异步操作(如
cudaMemcpyAsync+kernel<<<...>>>),计算与内存传输可并发执行,从而隐藏内存延迟。 - 多内核并发执行: 若硬件资源(如 SM 数量)充足,多个内核可在不同流中并发运行。
- 计算与内存传输的重叠:
通过不同流的异步操作(如
在硬件模型中的体现:
- GPU 资源调度: CUDA 运行时通过 调度器(Scheduler) 动态分配 SM 和内存带宽给不同流的任务。例如,NVIDIA Fermi 架构起支持最多 16 个并发内核。
- 内存带宽复用:
不同流的内存拷贝操作(如
cudaMemcpyAsync)可利用独立的 DMA 引擎并发执行。
CPU
概念: 并发是指多个任务在逻辑上同时执行,但物理上可能通过时间片轮转或资源复用的方式交替执行。即使单核 CPU 也能通过快速切换任务上下文实现并发。
在硬件中的体现:
- 单核 CPU:通过时间片轮转(Time-Slicing)快速切换任务,模拟“同时执行”。
- 多核 CPU:单核处理并发任务的同时,其他核心可并行执行其他任务。
在软件中的体现:
- 操作系统调度器:管理任务的切换和资源分配(如 CPU、内存),通过上下文切换实现并发。
- 并发编程模型:如多线程、异步 I/O(如 Java 的
CompletableFuture),通过协作式或抢占式调度实现任务交错执行。
示例:
- Web 服务器:单核 CPU 通过并发处理多个 HTTP 请求,快速切换上下文以响应客户端。
- 图形用户界面(GUI):主线程处理用户交互,后台线程执行耗时任务(如文件读取)。
3. 并发与并行的区别与联系
GPU
| 特性 | 并行(Parallelism) | 并发(Concurrency) |
|---|---|---|
| 物理性 | 真正同时执行(硬件支持) | 逻辑交替执行(通过时间片或资源复用) |
| 硬件依赖 | 需要多核/多处理单元(如 GPU 的 SM) | 通过流调度实现,硬件支持有限(如流数限制) |
| CUDA 实现方式 | 多线程块、多 warp、多内核 | CUDA Stream 管理异步操作 |
| 典型应用 | 科学计算、深度学习训练(大规模数据并行) | 实时系统、任务流水线(计算与传输重叠) |
CPU
| 特性 | 并行(Parallelism) | 并发(Concurrency) |
|---|---|---|
| 硬件依赖 | 多核 CPU 或分布式系统 | 单核或单核 + 多核 CPU(单核需时间片轮转) |
| 执行方式 | 物理上真正同时执行 | 逻辑上交替执行(可能通过时间片或资源复用) |
| 软件支持 | 需要并行编程模型(如 OpenMP、MPI) | 需要并发编程模型(如多线程、异步 I/O) |
| 目标 | 缩短任务总耗时(加速计算) | 提高资源利用率(如响应性、吞吐量) |
| 典型场景 | 科学计算、大规模数据处理(如 Hadoop、Spark) | I/O 密集型任务(如 Web 服务器、数据库) |
4. 实际应用中的对应关系
GPU
-
硬件模型:
- 并行:GPU 的多 SM 架构(如 NVIDIA A100 的 108 SM)直接支持线程级并行。
- 并发:GPU 的流调度器(如 ECU,Execution Context Unit)管理多个流的并发执行。
-
编程模型:
- 并行:通过线程块(Block)和网格(Grid)设计实现大规模数据并行。
- 并发:通过 CUDA Stream 和异步 API(如
cudaMemcpyAsync)实现任务级并发。
CPU
单核 CPU:
- 只能实现并发:通过时间片轮转切换任务(如早期 PC 的 Windows 9x 系统)。
- 示例:
- 一个进程运行 10ms 后被强制中断,切换到另一个进程。
- 用户感觉多个程序“同时运行”,但实际是快速切换的结果。
多核 CPU:
- 并发 + 并行:
- 操作系统调度器将任务分配到多个核心(并行),同时单个核心内仍通过时间片轮转处理并发任务。
- 示例:
- 4 核 CPU 上运行 8 个线程:4 个线程并行执行,另外 4 个线程在单核上并发执行。
5. 优化建议
GPU
-
最大化并行:
- 合理配置线程块大小(如 256~512 线程/块),确保 SM 资源利用率。
- 使用
__global__内核的<<<grid, block>>>配置充分填充 GPU。
-
利用并发:
- 为计算与内存传输分配不同流,通过
cudaMemcpyAsync+kernel<<<...>>>实现重叠。 - 使用 CUDA Graph 固化高频重复操作(如推理阶段的 Token 生成),减少 CPU 开销。
- 为计算与内存传输分配不同流,通过
CPU
-
硬件设计:
- 多核 CPU 的核心数量决定并行能力上限(如 8 核 CPU 最大支持 8 个并行任务)。
- 缓存层级(L1/L2/L3)影响并行任务的数据访问效率。
-
软件设计:
- 并发编程:需处理线程安全(如锁、信号量)和资源竞争问题。
- 并行编程:需关注任务划分(负载均衡)和通信开销(如 MPI 中的点对点通信)。
总结
GPU
- 并行:是 GPU 硬件的核心能力,通过多线程/多内核实现物理并行。
- 并发:是 CUDA 编程模型的高级特性,通过流调度实现逻辑上的任务重叠。
- 结合使用:两者结合可显著提升 GPU 利用率,例如在深度学习中通过并发流重叠数据加载与模型计算。
CPU
- 传统 CPU:单核仅支持并发,多核支持并发 + 并行。
- 操作系统:通过调度器管理任务的并发执行,并在多核环境中实现并行加速。
- 开发实践:
- I/O 密集型任务(如 Web 服务):优先利用并发(多线程/异步)。
- CPU 密集型任务(如科学计算):优先利用并行(多核/分布式)。