如果GPU这么好,为何还用CPU?


当我们说 GPU 的性能远高于 CPU 时,我们谈论的是一种称为TFLOPS的测量方法,它本质上衡量的是处理器在一秒钟内可以执行多少万亿次数学运算。例如,Nvidia A100 GPU 可以执行 9.7 TFLOPS(每秒 9.7 万亿次运算),而最近的英特尔 24 核处理器可以执行 0.33 TFLOPS。这意味着中等 GPU 的速度至少比最强大的 CPU 快 30 倍

但是我的 MacBook 中的芯片(Apple 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

在这种情况下,我们进行十次完全独立的乘法运算。重要的是顺序并不重要。如果你想和朋友分担工作,你可以说:“你乘奇数,我乘偶数。”你们可以分开工作,也可以同时工作,并获得准确的结果。

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

例如,假设我们有一个运行 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 具有少量大核心(Apple 的 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 仍然因其处理复杂逻辑和适应不断变化的条件的独特能力而必不可少。Apple 的 M3 等现代芯片兼具两者:将 CPU 灵活性与 GPU 计算能力相结合。

事实上,更精确的绘画视频版本将显示 CPU 管理图像下载和内存分配,然后调度 GPU 快速渲染像素。