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架构简图:
这张图里,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)的关系。
典型的协同模式是这样的:
- CPU负责控制流:加载数据、分配内存、启动核函数、处理结果。
- GPU负责计算:执行大量的并行计算任务。
- 数据传输:CPU和GPU之间通过PCIe总线传输数据。
我建议你记住这张表:
| 特性 | CPU | GPU |
|---|---|---|
| 核心数 | 4-16个 | 数千个 |
| 单核性能 | 强 | 弱 |
| 并行能力 | 弱 | 强 |
| 适合任务 | 串行、复杂逻辑 | 并行、简单运算 |
| 内存 | 大(GB级) | 相对小(GB级) |
| 延迟 | 低 | 高 |
在因子挖掘中,我一般这样分工:
- CPU:读取行情数据、因子预处理、结果回测、策略逻辑。
- GPU:因子计算(比如滚动窗口的均值、标准差、协方差矩阵等)。
避坑指南:我曾经做过一个项目,把数据从CPU拷贝到GPU,算完再拷回来。结果发现,数据传输的时间比计算时间还长!后来我改用异步传输和流水线技术,才把性能提上来。记住:数据传输是瓶颈,能少传就少传。
还有个细节:CPU和GPU是异步工作的。你启动一个核函数后,CPU不会等它执行完,而是继续往下走。如果你需要等待结果,得调用 cudaDeviceSynchronize()。这个函数,我刚开始经常忘记加,导致读到的数据是错的。
好了,这一章就到这里。GPU计算的基础,说白了就是三件事:理解GPU的并行架构、学会CUDA编程模型、掌握CPU和GPU的分工协作。后面咱们会深入每个细节,一步步把因子挖掘的速度提上去。
我的建议:如果你刚开始接触GPU,别急着写复杂的代码。先装好CUDA工具包,跑通几个官方示例,感受一下「并行」的感觉。磨刀不误砍柴工。