1. GPU计算基础:GPU架构概述、CUDA编程模型、GPU与CPU的协同工作模式

各位同学好,我是老张。在量化这个行当里摸爬滚打了十来年,今天咱们聊聊GPU计算的基础。说实话,我刚入行那会儿,觉得GPU就是用来打游戏的。直到有一次,我跑一个多因子回测,CPU算了一整夜没出结果,第二天被老板骂得狗血淋头。从那以后,我才真正开始研究GPU。

这一章,咱们不讲虚的。直接上干货,把GPU的底裤扒干净。

1.1 GPU架构概述:它凭什么这么快?

先问个问题:为什么GPU比CPU快那么多?

说白了,设计理念不同。CPU是「全能选手」,什么活都能干,但一次只能干几件事。GPU是「流水线工人」,只会干简单的活,但一次能同时干成千上万件事。

你看CPU,核心数一般4个、8个、16个,顶天了。但GPU呢?NVIDIA的A100有6912个CUDA核心。你想想看,6912个工人同时干活,那是什么概念?

我个人的理解是:CPU适合处理复杂的、串行的任务;GPU适合处理简单的、并行的任务。因子挖掘里,大量的矩阵运算、协方差计算,说白了就是重复的数学运算,这正是GPU的强项。

核心概念:GPU的架构核心是「大规模并行」。它通过成千上万个轻量级核心,同时处理数据的不同部分。每个核心虽然慢,但数量多,整体吞吐量惊人。

咱们来看一张图,这是我手绘的GPU架构简图:

GPU架构简图(以NVIDIA为例) GPU GPC 0 TPC 0 SM | SM TPC 1 SM | SM TPC 2 SM | SM GPC 1 TPC 0 SM | SM TPC 1 SM | SM TPC 2 SM | SM GPC 2 TPC 0 SM | SM TPC 1 SM | SM TPC 2 SM | SM SM = Streaming Multiprocessor(流多处理器) GPC = Graphics Processing Cluster

这张图里,SM(流多处理器)是最核心的单元。每个SM里包含多个CUDA核心、共享内存、寄存器等。我习惯把SM想象成一个「小车间」,里面有几个工人(CUDA核心)和一块公用的工作台(共享内存)。

我的经验:刚开始学GPU编程时,别纠结于SM内部的具体细节。你只需要知道:GPU有大量SM,每个SM能同时执行很多线程。这就够了。细节后面慢慢补。

1.2 CUDA编程模型:怎么让GPU干活?

好,现在你知道GPU长什么样了。那怎么让它干活呢?

NVIDIA搞了一套叫CUDA的东西。说白了,就是一套编程接口,让你能用C/C++写代码,然后让GPU去执行。

CUDA编程模型的核心,我总结为三个词:线程、块、网格

  • 线程(Thread):最小的执行单元。每个线程执行相同的代码,但处理不同的数据。
  • 线程块(Block):一组线程的集合。同一个块内的线程可以共享内存、同步执行。
  • 网格(Grid):一组线程块的集合。一个网格对应一个核函数(Kernel)的执行。

你想想看,这就像军队组织:网格是师,线程块是团,线程是士兵。每个士兵执行同样的命令(代码),但负责不同的阵地(数据)。

来看个最简单的例子:

// 核函数:两个向量相加
__global__ void vecAdd(float* A, float* B, float* C, int N) {
    int i = blockIdx.x * blockDim.x + threadIdx.x;
    if (i < N) {
        C[i] = A[i] + B[i];
    }
}

// 调用核函数
int main() {
    // ... 分配内存、拷贝数据 ...
    
    int threadsPerBlock = 256;
    int blocksPerGrid = (N + threadsPerBlock - 1) / threadsPerBlock;
    
    vecAdd<<<blocksPerGrid, threadsPerBlock>>>(d_A, d_B, d_C, N);
    
    // ... 拷贝结果回CPU ...
}

这段代码里,__global__ 表示这是一个核函数,在GPU上执行。<<<blocksPerGrid, threadsPerBlock>>> 指定了网格和块的大小。

注意:我曾经犯过一个低级错误——线程索引计算错了。当时算一个矩阵乘法,结果怎么都不对。查了两天才发现,是 blockIdx.x * blockDim.x + threadIdx.x 这个公式写成了 blockIdx.x * threadIdx.x。嗯,这种错误,犯一次就记住了。

1.3 GPU与CPU的协同工作模式:谁该干什么?

GPU再强,也不能单干。它需要CPU配合。就像老板(CPU)和员工(GPU)的关系。

典型的协同模式是这样的:

  1. CPU负责控制流:加载数据、分配内存、启动核函数、处理结果。
  2. GPU负责计算:执行大量的并行计算任务。
  3. 数据传输:CPU和GPU之间通过PCIe总线传输数据。

我建议你记住这张表:

特性 CPU GPU
核心数 4-16个 数千个
单核性能
并行能力
适合任务 串行、复杂逻辑 并行、简单运算
内存 大(GB级) 相对小(GB级)
延迟

在因子挖掘中,我一般这样分工:

  • CPU:读取行情数据、因子预处理、结果回测、策略逻辑。
  • GPU:因子计算(比如滚动窗口的均值、标准差、协方差矩阵等)。

避坑指南:我曾经做过一个项目,把数据从CPU拷贝到GPU,算完再拷回来。结果发现,数据传输的时间比计算时间还长!后来我改用异步传输和流水线技术,才把性能提上来。记住:数据传输是瓶颈,能少传就少传

还有个细节:CPU和GPU是异步工作的。你启动一个核函数后,CPU不会等它执行完,而是继续往下走。如果你需要等待结果,得调用 cudaDeviceSynchronize()。这个函数,我刚开始经常忘记加,导致读到的数据是错的。

好了,这一章就到这里。GPU计算的基础,说白了就是三件事:理解GPU的并行架构、学会CUDA编程模型、掌握CPU和GPU的分工协作。后面咱们会深入每个细节,一步步把因子挖掘的速度提上去。

我的建议:如果你刚开始接触GPU,别急着写复杂的代码。先装好CUDA工具包,跑通几个官方示例,感受一下「并行」的感觉。磨刀不误砍柴工。


专注资料整理