GPU 这么厉害,我们为什么还要用 CPU?

作者 | TEDDY WAHLE
译者 | 王强
策划 | 褚杏娟

最近,Twitter 上出现了一段 2009 年的老视频。它让观众可以直观地了解 CPU 和 GPU 之间的区别。

原始视频在此,时长 90 秒:https://youtu.be/-P28LKWTzrI

在这个视频中,CPU 和 GPU 两种处理器在一场绘画对决中正面交锋。两种处理器先后连接到了一台发射彩弹来作画的机器上,通过射击到屏幕上的彩蛋来绘制图像。

CPU 需要整整 30 秒才能画出一个非常基本的笑脸:

然后 GPU 瞬间就能画出一幅蒙娜丽莎的图像:

单纯从这段视频中可以得出一个结论:CPU 速度很慢,GPU 速度很快。虽然这是事实,但视频中并没有提到两种处理器的很多细微差别。

每秒万亿次浮点运算(TFLOPS)

当我们说 GPU 的性能比 CPU 高得多时,我们指的是一种称为 TFLOPS 的测量方法,它本质上是衡量处理器在一秒钟内可以执行多少万亿次数学运算。例如,Nvidia A100 GPU 可以执行 9.7 TFLOPS(每秒 9.7 万亿次浮点操作),而最新一代的 Intel 24 核处理器可以执行 0.33 TFLOPS。这意味着中等水平的 GPU 至少比最强大的 CPU 快 30 倍。

但我的 MacBook(苹果 M3 芯片)中的芯片同时包含了一个 CPU 和一个 GPU。为什么?我们不能放弃这些非常慢的 CPU 吗?

不同类型的程序

让我们定义两种类型的程序:顺序程序和并行程序。

顺序程序

顺序程序指的是所有指令都必须一个接一个运行的程序。以下是一个例子。

def sequential_calculation():    a = 0    b = 1
for _ in range(100):

a, b = b, a + b
return b

在这里,我们连续 100 次使用前两个数字来计算下一个数字。这个程序的一个重要特征是每个步骤都依赖于它之前的两个步骤。如果你手动做这个计算的话,你不能告诉朋友,“你计算第 51 步到第 100 步,而我从第 1 步开始”,因为他们需要第 49 步和第 50 步的结果才能开始计算第 51 步。每个步骤都需要知道序列中的前两个数字。

并行程序

并行程序是可以同时执行多个指令的程序,因为它们不依赖于彼此的结果。以下是一个例子:

def parallel_multiply():    numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]    results = []

for n in numbers: results.append(n * 2)

return results

在这个例子中,我们进行了 10 次完全独立的乘法运算。这里重要的是顺序无关紧要。如果你想和朋友分担工作,你可以说:“你乘奇数,我乘偶数。”你们可以分开工作,也可以同时工作,并得到准确的结果。

错误的二分法

实际上,这种划分是一种错误的二分法。大多数大型真实应用程序都混合了顺序和并行代码。事实上,每个程序都会有一定比例的指令是可并行化的。

例如,假设我们有一个运行 20 次计算的程序。前 10 个是斐波那契数,必须按顺序计算,但后 10 个计算可以并行运行。我们会说这个程序是“50% 可并行化的”,因为一半的指令可以独立完成。为了说明这一点:

def half_parallelizeable():    # Part 1: Sequential Fibonacci calculation    a, b = 0, 1    fibonacci_list = [a, b]    for _ in range(8):  # Calculate 8 more numbers        a, b = b, a + b        fibonacci_list.append(b)

# Part 2: Each step is independent parallel_results = [] for n in fibonacci_list: parallel_results.append(n * 2)
return fibonacci_list, parallel_results

前半部分必须是连续的——每个斐波那契数都取决于它前面的两个数。但后半部分可以采用完整的列表并独立地将每个数字加倍。

如果不先计算第 6 和第 7 个数,你就无法计算第 8 个斐波那契数,但一旦有了完整的序列,你就可以将加倍操作分配给尽可能多的可用工作器。

不同程序类型适合不同的处理器

广义上讲,CPU 更适合顺序程序,而 GPU 更适合并行程序。这是因为 CPU 和 GPU 之间存在根本的设计差异。

CPU 具有少量较大的核心(苹果 M3 具有 8 核 CPU),而 GPU 有许多小的核心(Nvidia 的 H100 GPU 有数千个核心)。

这就是 GPU 擅长运行高度并行的程序的原因——它们有数千个简单核心,可以同时对不同的数据执行相同的操作。

渲染视频游戏图形是一种需要进行许多简单重复计算的应用程序。想象一下你的视频游戏屏幕是一个巨大的像素矩阵。当你突然将角色向右转动时,所有这些像素都需要重新计算为新的颜色值。所幸屏幕顶部像素的计算与屏幕底部像素的计算是独立的。因此,计算可以分散到数千个 GPU 核心上。这就是 GPU 对游戏如此重要的原因所在。

CPU 擅长处理随机事件

在高度并行的任务(例如将 10,000 个独立数字的矩阵相乘)方面,CPU 比 GPU 慢得多。但是,它们擅长复杂的顺序处理和复杂的决策。

将 CPU 核心想象成繁忙餐厅厨房里的主厨。这位厨师可以:

  • 当有特殊饮食要求的 VIP 客人到来时,立即调整烹饪计划

  • 在准备精致酱汁和检查烤蔬菜之间无缝切换

  • 通过重新组织整个厨房工作流程来处理停电等意外情况

  • 安排多道菜的烹饪过程,让它们在恰当的时刻热腾腾地送到

  • 在处理数十个完成状态各异的订单同时保持食物质量

相比之下,GPU 核心就像一百名擅长重复任务的流水线厨师——他们可以在两秒钟内切好洋葱,但无法有效地管理整个厨房。如果你要求 GPU 处理不断变化的晚餐服务需求,它会很吃力。

这就是为什么 CPU 对于运行计算机操作系统至关重要的原因所在。现代计算机面临着一系列不可预测的事件:应用程序启动和停止、网络连接断开、文件被访问以及用户在屏幕上随机点击。CPU 擅长处理所有这些任务,同时保持系统响应能力。它可以立即从帮助 Chrome 渲染网页切换到处理 Zoom 视频通话,再到处理新的 USB 设备连接——同时跟踪系统资源并确保每个应用程序都得到应有的关注。

因此,虽然 GPU 擅长并行处理,但 CPU 仍然因其处理复杂逻辑和适应不断变化的条件的独特能力而保持着不可或缺的地位。像苹果 M3 这样的现代芯片兼具两者:将 CPU 灵活性与 GPU 计算能力相结合在一起。

事实上,能够更准确反映实际情况的绘画工作对比视频会显示出 CPU 管理图像的下载和内存分配工作,然后再调度 GPU 快速渲染像素。

原文链接:

https://codingstuff.substack.com/p/if-gpus-are-so-good-why-do-we-still

(文:AI前线)

欢迎分享

发表评论