跳转至

GPU与并行计算

概述

GPU(图形处理单元)最初为图形渲染设计,但其大规模并行架构使其成为深度学习推理和传感器数据处理的理想加速器。在机器人系统中,GPU负责视觉感知、点云处理和神经网络推理等计算密集型任务。

GPU架构基础

CPU vs GPU:设计哲学

特性 CPU GPU
核心数 4-16(大核) 数百至数千(小核)
时钟频率 2-5 GHz 1-2 GHz
缓存 大(数十MB L3) 小(数MB L2)
控制逻辑 复杂(分支预测、乱序执行) 简单
适合任务 复杂逻辑、低延迟 大规模并行、高吞吐
设计目标 最小化延迟 最大化吞吐量
CPU: 少量大核心,优化延迟
┌──────────────┐  ┌──────────────┐
│   大核心 1    │  │   大核心 2    │
│ [控制] [ALU] │  │ [控制] [ALU] │
│ [L1 Cache]   │  │ [L1 Cache]   │
└──────────────┘  └──────────────┘
        ┌──────────────────┐
        │    大 L3 Cache    │
        └──────────────────┘

GPU: 大量小核心,优化吞吐
┌────┐┌────┐┌────┐┌────┐┌────┐┌────┐┌────┐┌────┐
│core││core││core││core││core││core││core││core│ ...
└────┘└────┘└────┘└────┘└────┘└────┘└────┘└────┘

NVIDIA GPU架构

NVIDIA GPU的核心组织结构:

Streaming Multiprocessor (SM)

SM是GPU的基本计算单元,每个SM包含:

  • CUDA核心(FP32):执行浮点运算
  • Tensor核心:专用矩阵运算加速器
  • 共享内存(Shared Memory):SM内线程共享的低延迟存储
  • 寄存器文件:大量寄存器支持线程上下文
  • Warp调度器:管理线程束的执行
graph TB
    subgraph GPU
        subgraph SM_0[SM 0]
            A1[CUDA Cores x128]
            A2[Tensor Cores x4]
            A3[Shared Memory 128KB]
            A4[Warp Schedulers x4]
        end
        subgraph SM_1[SM 1]
            B1[CUDA Cores x128]
            B2[Tensor Cores x4]
            B3[Shared Memory 128KB]
            B4[Warp Schedulers x4]
        end
        SM_N[... SM N]
        L2[L2 Cache]
        MEM[Global Memory / VRAM]
    end

    SM_0 --> L2
    SM_1 --> L2
    SM_N --> L2
    L2 --> MEM

SIMT执行模型

GPU采用SIMT(Single Instruction, Multiple Threads)执行模型:

  • 线程以Warp(32个线程)为单位执行
  • 同一Warp内的线程执行相同的指令
  • 但可以操作不同的数据(不同地址、不同寄存器)
\[ \text{GPU总线程数} = \text{SM数量} \times \text{每SM最大线程数} \]

Warp分歧(Warp Divergence)

当Warp内的线程遇到条件分支走向不同路径时,GPU必须串行执行两条路径。这会严重降低效率:

if (threadIdx.x % 2 == 0) {
    // 偶数线程执行这里
} else {
    // 奇数线程执行这里
}
// 最坏情况:性能减半

CUDA编程基础

线程层次

CUDA采用分层的线程组织:

层次 说明 对应硬件
Thread 最小执行单位 CUDA核心
Warp 32个线程 SM中的Warp调度单位
Block 多个Warp 映射到一个SM
Grid 多个Block 整个GPU

内存层次

内存类型 大小 延迟 作用域
寄存器 ~256KB/SM 1周期 单线程
共享内存 64-228KB/SM ~5周期 Block内共享
L1 Cache 与共享内存共享 ~30周期 SM
L2 Cache 数MB ~200周期 全GPU
Global Memory (VRAM) 4-80GB ~400周期 全GPU

CUDA Kernel示例

// 向量加法 - 最基本的CUDA kernel
__global__ void vectorAdd(float* A, float* B, float* C, int N) {
    int idx = blockIdx.x * blockDim.x + threadIdx.x;
    if (idx < N) {
        C[idx] = A[idx] + B[idx];
    }
}

// 启动kernel
int threadsPerBlock = 256;
int blocksPerGrid = (N + threadsPerBlock - 1) / threadsPerBlock;
vectorAdd<<<blocksPerGrid, threadsPerBlock>>>(d_A, d_B, d_C, N);

图像处理示例

// 图像灰度化 - 机器人视觉预处理
__global__ void rgb2gray(unsigned char* rgb, unsigned char* gray, 
                         int width, int height) {
    int x = blockIdx.x * blockDim.x + threadIdx.x;
    int y = blockIdx.y * blockDim.y + threadIdx.y;

    if (x < width && y < height) {
        int idx = y * width + x;
        int rgb_idx = idx * 3;
        // ITU-R BT.601 标准
        gray[idx] = (unsigned char)(0.299f * rgb[rgb_idx] + 
                                     0.587f * rgb[rgb_idx+1] + 
                                     0.114f * rgb[rgb_idx+2]);
    }
}

// 2D grid启动
dim3 block(16, 16);
dim3 grid((width+15)/16, (height+15)/16);
rgb2gray<<<grid, block>>>(d_rgb, d_gray, width, height);

并行加速理论

理论加速比

理想情况下,\(N\) 个处理单元的加速比:

\[ S_{\text{ideal}} = N \]

实际加速比受限于:

\[ S_{\text{actual}} = \frac{T_{\text{serial}}}{T_{\text{parallel}}} = \frac{T_{\text{serial}}}{T_{\text{serial\_part}} + \frac{T_{\text{parallel\_part}}}{N} + T_{\text{overhead}}} \]

其中 \(T_{\text{overhead}}\) 包括:

  • 线程创建和同步开销
  • 内存拷贝开销(CPU↔GPU)
  • 负载不均衡

Gustafson定律

与Amdahl定律(固定问题规模)不同,Gustafson定律考虑随处理器数增加扩展问题规模

\[ S_G = N - \alpha(N-1) \]

其中 \(\alpha\) 是串行部分的比例。这解释了为什么GPU在大规模数据处理中如此有效。

TensorRT推理加速

TensorRT优化流程

TensorRT是NVIDIA的深度学习推理优化器:

graph LR
    A[PyTorch/ONNX模型] --> B[TensorRT解析]
    B --> C[层融合<br>Layer Fusion]
    C --> D[精度校准<br>FP16/INT8]
    D --> E[Kernel自动调优<br>Auto-Tuning]
    E --> F[优化后的引擎<br>TRT Engine]

关键优化技术

技术 说明 典型加速比
层融合 合并Conv+BN+ReLU为一个kernel 1.5-2x
FP16推理 半精度浮点 2-3x
INT8量化 8位整数推理 3-5x
动态batch 自动选择最优batch size 1.2-1.5x

推理性能示例

在Jetson Orin NX上的典型推理性能:

模型 FP32 FP16 INT8 用途
YOLOv8n 45 FPS 120 FPS 200 FPS 目标检测
YOLOv8s 25 FPS 70 FPS 130 FPS 目标检测
MobileNetV3 200 FPS 500 FPS 800 FPS 图像分类
PointPillars 15 FPS 35 FPS 60 FPS 3D目标检测

Jetson GPU规格对比

特性 Orin Nano Orin NX AGX Orin
GPU架构 Ampere Ampere Ampere
CUDA核心 1024 2048 2048
Tensor核心 32 64 64
GPU频率 625 MHz 918 MHz 1.3 GHz
AI算力 (INT8) 40 TOPS 100 TOPS 275 TOPS
显存 8GB共享 8/16GB共享 32/64GB共享
功耗 7-15W 10-25W 15-60W

GPU编程最佳实践

1. 最大化占用率

确保足够多的线程来隐藏内存延迟:

\[ \text{占用率} = \frac{\text{活跃Warp数}}{\text{SM最大Warp数}} \]

目标占用率 > 50%。

2. 合并内存访问

相邻线程访问相邻内存地址:

// 好:合并访问(Coalesced)
C[threadIdx.x] = A[threadIdx.x] + B[threadIdx.x];

// 差:跨步访问(Strided)
C[threadIdx.x] = A[threadIdx.x * stride];

3. 减少CPU-GPU数据传输

# 差:频繁拷贝
for frame in camera_stream:
    gpu_frame = frame.to('cuda')     # CPU -> GPU
    result = model(gpu_frame)         # GPU推理
    cpu_result = result.to('cpu')     # GPU -> CPU

# 好:流水线化,使用CUDA Stream
stream1 = torch.cuda.Stream()
stream2 = torch.cuda.Stream()
# 在stream1上传输数据的同时,stream2上执行推理

4. 使用Tensor核心

利用Tensor核心加速矩阵运算:

\[ D = A \times B + C \quad \text{(矩阵乘加)} \]

Tensor核心在一个时钟周期内完成 \(4 \times 4\) 矩阵的乘加运算。

在PyTorch中启用Tensor核心

# 使用FP16自动混合精度
with torch.cuda.amp.autocast():
    output = model(input)

机器人视觉处理流水线

graph LR
    A[Camera<br>CSI/USB] --> B[CPU<br>图像解码]
    B --> C[GPU<br>预处理<br>resize/normalize]
    C --> D[GPU<br>神经网络推理<br>检测/分割]
    D --> E[GPU<br>后处理<br>NMS/解码]
    E --> F[CPU<br>结果发布<br>ROS2 Topic]

使用GStreamer + DeepStream可以实现全GPU流水线,避免CPU-GPU之间的数据拷贝。

小结

  1. GPU通过大规模并行弥补单线程性能的不足
  2. SIMT模型要求避免Warp分歧
  3. 内存层次合并访问对GPU性能至关重要
  4. TensorRT可以将推理性能提升3-5倍
  5. 减少CPU-GPU数据传输是优化的关键
  6. Jetson系列为机器人提供了从40到275 TOPS的AI算力选择

参考资料


评论 #