第一章:GPU架构演进史——从固定功能管线到统一着色器架构
大家好,我是你们的老朋友。今天咱们聊聊GPU的进化史。
说实话,我入行那会儿,GPU还是个“傻大个”。它只会干一件事——画图。你给它一堆三角形,它老老实实给你渲染出来。别的?别指望了。
但你看现在,GPU成了并行计算的巨兽。训练大模型、跑科学计算、挖矿……什么都能干。这中间到底发生了什么?
嗯,咱们从头捋一捋。
1.1 固定功能管线时代:GPU的“石器时代”
90年代末,GPU还是个纯粹的图形加速器。它的工作流程是固定的,就像一条流水线,每个环节干固定的活。
这条流水线长这样:
顶点输入 → 顶点变换 → 图元装配 → 光栅化 → 片段着色 → 输出合并
每个环节都是硬件写死的。你没法改,也没法跳过去。
我印象最深的是,那时候做游戏特效,想实现一个“水面波纹”的效果。程序员只能在CPU端算好顶点位置,再传给GPU。每帧都要算,CPU累得半死,GPU却闲得发慌。
为什么会这样?因为GPU太“死板”了。它只认固定的指令集,你没法让它干点“出格”的事。
固定功能管线的核心特点:
- 每个阶段功能固定,不可编程
- 数据流单向,不可回读
- 开发者只能通过参数调节,无法自定义算法
说白了,那时候的GPU就是个“高级画图卡”。你给它什么,它画什么。你想让它算点别的?门儿都没有。
1.2 可编程管线的萌芽:GPU开始“开窍”了
2001年左右,NVIDIA和ATI(后来被AMD收购)开始琢磨:能不能让GPU干点更灵活的事?
于是,顶点着色器和片段着色器诞生了。
这两个东西,说白了就是给GPU装上了“小脑袋”。开发者可以写一小段程序,告诉GPU怎么处理顶点、怎么计算颜色。
我记得当时NVIDIA的GeForce 3是第一块支持可编程着色器的消费级GPU。我拿到开发板那天,兴奋得一夜没睡。终于可以自己定义光照算法了!
但问题也来了:
- 顶点着色器和片段着色器是分开的硬件单元
- 它们的指令集不通用
- 资源分配不灵活——顶点多的场景,顶点着色器忙死,片段着色器闲死;反之亦然
你想想看,一个游戏场景里,如果模型很复杂(顶点多),但特效简单(片段少),那顶点着色器就累趴了,片段着色器却在旁边看戏。反过来也一样。
这种“旱的旱死,涝的涝死”的局面,让硬件利用率大打折扣。
避坑指南:我曾经在一个项目中,为了平衡顶点和片段的负载,不得不把一些计算从顶点着色器挪到片段着色器。结果发现,不同GPU的负载特性完全不一样。有的GPU顶点单元强,有的片段单元强。没有万能的优化方案,必须针对具体硬件调优。
1.3 统一着色器架构:GPU的“觉醒时刻”
2006年,NVIDIA发布了GeForce 8800 GTX。这块卡,改变了游戏规则。
它采用了统一着色器架构(Unified Shader Architecture)。
什么意思呢?就是把顶点着色器和片段着色器合并成一种通用的计算单元——流处理器(Stream Processor)。
这些流处理器长得一模一样,能干所有活。顶点计算、片段计算、几何计算……全都能干。
这样一来,负载均衡的问题就解决了。顶点多的时候,所有流处理器都去算顶点;片段多的时候,所有流处理器都去算片段。再也没有“闲人”了。
| 特性 | 固定功能管线 | 分离式可编程管线 | 统一着色器架构 |
|---|---|---|---|
| 计算单元 | 专用硬件 | 顶点/片段分离 | 通用流处理器 |
| 编程灵活性 | 几乎为零 | 有限 | 高 |
| 负载均衡 | 固定 | 差 | 好 |
| 代表产品 | GeForce 2 | GeForce 3/4/5/6/7 | GeForce 8800 GTX |
我个人习惯把统一着色器架构比作“瑞士军刀”。以前你需要带三把刀——切水果的、削木头的、开瓶盖的。现在一把刀全搞定,而且随时可以切换功能。
1.4 从图形到通用计算:GPU的“跨界之旅”
统一着色器架构带来的另一个革命性变化是:GPU终于可以用来做通用计算了。
因为流处理器是通用的,你完全可以写一段程序,让它去算矩阵乘法、做物理模拟、甚至破解密码。
2007年,NVIDIA推出了CUDA(Compute Unified Device Architecture)。这玩意儿,说白了就是给GPU装上了“编程语言”。开发者可以用C语言直接写GPU程序,不用再伪装成图形渲染了。
我记得第一次用CUDA做并行归约(Parallel Reduction)的时候,看到GPU把CPU甩开几十倍的速度,整个人都傻了。原来计算可以这么快!
但这里有个坑:
注意:GPU不是万能的。它擅长的是“数据并行”——同样的操作,对大量数据同时执行。如果你要处理的是“任务并行”——不同的操作,对少量数据执行——那GPU反而可能比CPU慢。因为GPU的单个线程很弱,它靠的是“人多力量大”。
1.5 架构演进的关键里程碑
咱们用时间线捋一下关键节点:
- 1999年:GeForce 256,首款“GPU”命名,固定功能管线
- 2001年:GeForce 3,引入可编程顶点/片段着色器
- 2006年:GeForce 8800 GTX,统一着色器架构,流处理器诞生
- 2007年:CUDA发布,GPU通用计算时代开启
- 2010年:Fermi架构,引入L1/L2缓存,更适合计算
- 2017年:Volta架构,引入Tensor Core,专为AI计算优化
你看,从固定功能到统一着色器,GPU走了将近10年。但这10年的积累,让GPU从一个“画图卡”变成了“计算巨兽”。
1.6 对开发者的启示
说了这么多历史,对我们写代码有什么启发?
我觉得有三点:
- 理解硬件才能写出好代码:你知道GPU是统一着色器架构,就知道为什么“分支”是性能杀手——因为所有流处理器执行同样的指令,遇到分支就得串行化。
- 负载均衡是永恒的主题:不管是图形渲染还是通用计算,让所有计算单元都忙起来,才是性能最大化的关键。
- 架构决定上限:你代码写得再漂亮,如果硬件架构不支持,那也是白搭。所以,了解GPU的演进史,就是了解性能优化的“天花板”在哪里。
嗯,第一章就聊到这儿。下一章咱们深入GPU的指令流水线,看看一条指令从发射到完成,到底经历了什么。
到时候见。