
多核处理器是现代计算的心脏,从智能手机到超级计算机无不如此。然而,普遍认为“核心越多速度越快”的观点是一种极大的过度简化。这种看法掩盖了一个复杂的世界,其中充满了物理约束、理论极限以及利用真正并行能力所需的复杂软件编排。要超越这个简单的类比,我们必须更深入地探究这些微小的“大脑”究竟是如何协同工作的。
本文将带领读者全面深入 CPU 核心的世界,旨在弥合硬件潜力与实际性能之间的鸿沟。通过探索基本概念,您将对硬件与软件之间错综复杂的协作关系有更深刻的理解。我们将从“原理与机制”章节开始,剖析核心本身,审视其物理本质、操作系统的关键角色以及支配其效率的基本定律。随后,“应用与跨学科联系”章节将揭示这些原理如何在真实场景中体现,从云端的资源调度到科学模拟的宏大挑战,凸显计算与其他领域之间深刻的联系。
要真正领会多核带来的革命,我们必须超越“越多越好”的简单观念。我们需要提出更深层次的问题。从根本上说,核心是什么?我们如何让一个计算机程序使用不止一个核心?又有哪些宇宙法则和现实约束限制着我们对无限速度的追求?让我们踏上这段旅程,层层剥开复杂性的外衣,揭示支配多核世界的那些优雅而时而令人沮撮的原理。
我们喜欢将 CPU 核心视为一个“大脑”。它执行指令,进行计算,并做出决策。很自然地,拥有更多的大脑应该能让我们思考得更快,或同时思考更多的事情。这正是并行计算的基本承诺。但这个简单的类比掩盖了深层的物理现实。核心并非一个抽象概念,它是由数百万乃至数十亿个晶体管蚀刻在一小片硅晶圆上构成的迷宫。而这个迷宫的建造方式至关重要。
想象一下,你正在打造一款新的智能设备。你需要一个处理器。你可以选择 FPGA(现场可编程门阵列),一种“可编程芯片”。在这块芯片上,你有两种处理器选择。你可以使用硬核(hard core),这是由制造商设计并作为固定、专用的硅块集成到芯片中的处理器。它就像直接从工厂购买一台高性能引擎。由于其每个部分都为特定任务经过了精心的优化,因此在同等尺寸下,它的速度和效率都令人难以置信。其架构是固定的,但性能超群。
或者,你也可以构建一个软核(soft core)。在这种方式下,你使用 FPGA 的通用可编程逻辑,通过硬件描述语言来从零开始构建自己的处理器。这就像用一套通用零件套件来打造你自己的引擎。其优势在于巨大的灵活性:你可以改变引擎的设计,添加自定义功能,或使其完美地适应某种独特的算法。但代价是高昂的。用通用零件构建的引擎永远不会像工厂优化的模型那样快速、小巧或节能。
硬核与软核之间的这种权衡揭示了第一条原则:CPU 核心是一个物理实体,受制于性能、功耗和灵活性之间的工程权衡。没有一个“最好”的核心,只有最适合特定任务和约束条件的核心。
假设我们有一块芯片,上面有八个设计精美、由工厂优化的硬核。我们为性能付出了代价。那么,一个程序,比如说你的网页浏览器,实际上是如何使用它们的呢?如果你运行一个在单核处理器时代编写的老程序,你会发现它固执地只在一个核心上运行,而其他七个核心完全处于空闲状态。硬件就在那里,但软件却对其视而不见。
这正是操作系统(OS)登场的时刻。操作系统是硬件的总指挥。它管理哪些程序运行、在哪里运行以及运行多长时间。为了释放多核的威力,操作系统需要一种方法,将任务视为可以分配给不同核心的独立执行线程。
设计程序线程与操作系统之间关系的方式有多种。在一种称为多对一线程的陈旧且现已基本淘汰的模型中,一个程序可能会创建数百个自己的“用户级”线程,但操作系统将它们全部视为一个单一实体,即一个“内核线程”。然后,操作系统将这个内核线程分配给单个核心。程序的内部调度器可以在其用户线程之间非常快速地切换,但由于整个线程组被限制在一个核心上,因此无法实现真正的并行。其他七个核心仍然处于休眠状态。
现代的解决方案是一对一线程模型。在此模型中,每个用户线程都映射到其自己的内核线程。当你的浏览器创建一个新线程来加载图片时,操作系统会将其视为一个可以调度的新独立任务。如果有 32 个活动线程和 8 个核心,操作系统可以完美地并行运行其中 8 个,每个核心一个。该模型的线程管理开销稍高,但与解锁机器并行硬件所带来的巨大收益相比,这点成本微不足道。没有硬件与操作系统线程模型之间的这种关键合作,多核处理器只不过是一个附带了大量昂贵而无用硅片的单核处理器。
好了,我们的操作系统支持多核,并且我们已经重写了程序以使用多个线程。如果我们从 1 个核心增加到 16 个核心,我们的程序能期望运行速度提高 16 倍吗?答案由计算机架构师 Gene Amdahl 在 20 世纪 60 年代发现,是一个响亮的“不”。
Amdahl 的洞察力,现已成为阿姆达尔定律,既简单又深刻。任何任务都由两种类型的工作组成:可并行化部分,可以分配给多个工作者(核心);以及串行部分,必须由一个工作者完成。想象一个经济模拟,在每个模拟日,数百万个个体代理更新自己的状态——这是一个完全并行的任务。但在更新之后,必须运行一个单一的全局市场出清计算来确定下一天的价格。这个计算是串行的;它必须等到所有代理都完成后才能开始,并且不能被拆分。
假设代理更新在单个核心上需要 80 秒,市场出清需要 20 秒。总时间是 100 秒。串行部分占比为 。即使有无限个核心,我们可以让 80 秒的并行部分几乎不花时间。但 20 秒的串行部分依然存在。总时间永远不可能少于 20 秒。因此,最大可能加速比是 。加速比受限于串行部分占比的倒数:。即使有 16 个核心,加速比也只是一个较为温和的 。阿姆达尔定律告诉我们,程序的串行部分就像一个不可移动的锚,永远束缚着其性能,并随着我们增加更多核心而产生递减的回报。
这种串行瓶颈以多种形式出现。考虑一个有许多线程试图访问共享数据库的系统。为防止数据损坏,访问由一个排他锁保护。一次只有一个线程可以持有该锁。即使有 64 个线程准备工作和 8 个核心可用,锁内部的代码部分也成为了一个串行瓶颈。所有 64 个数据库操作都必须一个接一个地发生。这突显了一个关键的区别:并发不是并行。并发意味着有许多任务在一段时间内取得进展。并行意味着同时执行许多任务。受锁保护的数据库允许高并发性,但在临界区内的并行性为零。
瓶颈并不总是一个锁。在一个繁忙的 Web 服务器中,瓶颈可能是 CPU 核心,也可能是服务器通过其网络接口卡 (NIC) 发送数据的速率。如果一个服务器有 8 个核心,能够处理约 个请求/秒,一个锁允许约 个请求/秒,但其网卡每秒只能发送 个请求的数据,那么系统的最大吞吐量就是 个请求/秒。网络是瓶颈,在这种速率下,CPU 的利用率仅为 16%。一个并行系统就像一条链条,其强度由其最薄弱的环节决定。
阿姆达尔定律尽管强大,但它仍然依赖于一个简化的世界观。它假设任务的并行部分可以无额外成本地分配给各个核心。现实要险恶和有趣得多。核心不是孤立的大脑;它们是数字车间里相互连接的工人,它们需要通信。这种通信通过共享内存进行。而且它不是免费的。
想象一个看似简单的并行算法,比如奇偶排序,它的工作方式是让许多处理器同时比较和交换数组中的相邻数字。在理想化模型中,这看起来很棒,有望获得与核心数量成正比的加速比。但在真实的多核 CPU 上,它可能会慢得灾难性。
原因在于内存层级结构。每个核心都有自己的小型、快速的缓存内存,用于保存常用数据的副本。当两个核心需要处理数组中的相邻元素时,比如核心 1 处理 ,核心 2 处理 ,这两个元素通常位于同一个缓存行中——即在主内存和缓存之间移动的内存块。如果核心 1 写入 ,缓存一致性协议——即保持所有缓存一致的规则集——必须使核心 2 缓存中该缓存行的副本失效。片刻之后,核心 2 需要写入 ,于是它必须重新取回该缓存行。这种对缓存行所有权的无休止来回传递,被称为缓存行乒乓效应,会使内存互连饱和,导致系统爬行。理论上的并行性被通信开销所淹没。
数据的这种“地理位置”在更大尺度上也同样重要。在具有多个处理器插槽的高性能服务器中,我们会遇到非一致性内存访问 (NUMA)。核心可以非常快速地访问直接连接到其自身插槽的内存(本地内存)。但要访问连接到另一个插槽的内存(远程内存),请求必须穿过一个较慢的互连。这就像你的个人工具箱就在身边,而要借用一个工具则需要穿过整个工厂车间。访问时间是不一致的。智能软件必须能够感知 NUMA,尽量将数据保存在离使用它最频繁的核心最近的内存节点上,甚至在访问模式改变时迁移数据,以最小化这些昂贵的远程访问 ([@problem_o:3230263])。
几十年来,芯片设计师从一个称为Dennard 缩放的原理中享受着“免费午餐”。随着晶体管变得越来越小(正如摩尔定律所预测的),它们的功率密度保持不变。这意味着我们可以在芯片上塞进越来越多的晶体管,并以更高的频率运行它们,而不会导致芯片熔化。我们可以在每一代产品中拥有更多、更快的核心。
大约在 2006 年,这顿免费午餐结束了。随着晶体管变得极小,量子效应导致它们即使在空闲时也会漏电。Dennard 缩放定律失效了。我们仍然可以添加更多的晶体管,但我们再也无法在不超过安全功率预算(由我们的芯片散热能力决定)的情况下同时为所有晶体管供电。
这产生了一个被称为暗硅(dark silicon)的范式转变问题。想象一下,建造一座可容纳十亿人口的城市,但电力只够同时为几个街区供电。城市的其余部分必须保持黑暗。在现代芯片上,我们可以制造数十亿个晶体管,足以容纳数十甚至数百个核心。但在任何给定时刻,我们只能承受为其中一小部分供电。
我们如何决定“点亮”芯片的哪一部分?这导致了异构计算的兴起。一块芯片可能包含几种不同类型的核心:几个用于延迟敏感任务的大而强的 CPU 核心,许多用于并行数据处理的小而高效的 GPU 核心,以及一个用于 AI 任务的专用神经网络加速器 (NNA)。当一个工作负载到达时,系统必须做出选择。为了满足例如 的严格功率预算,它可能被迫开启 CPU 和 NNA 以达到性能目标,同时让耗电的 GPU 保持休眠。多核时代的挑战不再仅仅是如何构建更多的核心,而是如何智能地管理一个广阔、强大但大部分时间处于休眠状态的硅片版图。
谁是这个极其复杂的管弦乐队的指挥?我们有异构核心、NUMA 内存地理、功率预算和串行瓶颈。负责让这一切混乱运转的实体,再次是操作系统。现代操作系统的调度器是计算机科学的杰作,它不断地解决那些能让人头晕目眩的难题。
考虑一下经典的优先级反转问题。一个高优先级线程 H 需要运行。但它在等待一个由低优先级线程 L 持有的锁。这已经很糟糕了。但在多核系统上,情况会变得更糟。假设 H 在核心 0 上,L 在核心 1 上。一个中优先级线程 M,也在核心 1 上,准备好运行。核心 1 上的调度器看到 M 的优先级高于 L,于是它抢占 L 并运行 M。结果是灾难性的:高优先级线程 H 被无限期阻塞,不是被它所等待的低优先级线程阻塞,而是被一个不相关的中优先级线程阻塞。
为了解决这个问题,调度器实现了复杂的协议,如优先级继承协议 (PIP)。当 H 在锁上阻塞时,操作系统会暂时将 L 的优先级提升到与 H 相等。现在,核心 1 上的调度器看到 L 具有最高优先级并运行它,使其能够快速完成其临界区并为 H 释放锁。操作系统甚至可能暂时将 L 迁移到一个空闲的核心以加速此过程。这些复杂的优先级和线程迁移之舞在你的电脑内部每秒发生数千次,所有这些都是为了兑现一个简单的承诺:最重要的工作最先完成。
从硅的物理学到算法的抽象定律,再到操作系统的复杂逻辑,CPU 核心的世界是一幅由相互关联的原理构成的美丽织锦。这是一个关于巨大力量和根本限制的故事,硬件能力的每一次飞跃都为必须驾驭它的软件带来了新的、更引人入胜的挑战。
你可能会认为 CPU 核心是一个深埋在硅芯片中的微型、超高速计算器。你没有错。但对于物理学家、工程师或生物学家来说,它的意义不止于此。单个核心就像一个勤奋的工人。那么,一个现代多核处理器就是这些工人的一个团队。连接数十个领域的深刻而美妙的问题是:你如何管理这个团队?你如何分派任务,协调他们的努力,让他们构建出宏伟的成果——无论是渲染你正在观看的视频,运行一个全球金融市场,还是模拟一颗恒星的诞生?
本章是一次穿越编排这些无声工人艺术与科学的旅程。我们将看到,拥有多个核心这一简单事实迫使我们直面优化、调度、系统设计甚至科学哲学本身的深层问题。
在最基本的层面上,一台拥有多个核心的计算机是一个有限资源的系统。这不是一个新问题;这是经济学和后勤学的经典挑战。想象一下,你是一名数据中心经理。你有一台服务器,拥有固定数量的 CPU 核心和固定数量的内存。两个不同的应用程序需要运行,每个都有其对这些资源的特定需求。你最多可以同时运行每个应用的多少个实例?
这个问题定义了一个操作的“可行域”。所有应用程序使用的 CPU 核心总数不能超过服务器所拥有的,内存也是如此。这些约束在一个抽象的可能性空间中勾勒出一个几何形状。保持在这个形状的边界内是游戏的第一条规则。这是一个简单而优雅的图景,它将我们硬件的物理限制与线性规划的数学领域联系起来,使我们能够精确地推理资源分配。
现在,让它变得更动态一些。想象一下,不再是两个稳定的应用程序,而是一系列不同的计算作业流式到达一个云计算集群,每个作业都有自己的 CPU 和内存需求。我们的目标是使用最少数量的服务器。这是一个远为复杂的谜题,计算机科学家称之为“装箱问题”。作业是大小和维度各异的物品,而服务器是我们必须将它们装入的箱子。这个问题是出了名的难以完美解决,所以我们依赖于巧妙的策略,或称启发式算法。例如,一个简单的规则可能是总是先处理最大的作业,并尝试将每个新作业放入第一个有空间的服务器中。这类源于计算复杂性理论的策略是使云计算在经济上可行的无形引擎,确保全球数据中心中数百万个核心得到有效利用。
当时间成为一个因素时,挑战变得更加尖锐。操作系统中的一些任务是不可中断的“临界区”。它们有固定的开始和结束时间。你不能暂停它们或移动它们。如果两个这样的任务在时间上重叠,它们绝对不能在同一个核心上运行。那么,要调度一组给定的这些关键任务,你需要的核心数量的绝对最小值是多少?这个问题揭示了与一个抽象数学领域——图论——的惊人联系。如果你将每个任务表示为一个点(一个顶点),并在任何两个时间上重叠的任务之间画一条线,你就创建了一个所谓的区间图。将任务分配给核心的问题就等同于对图进行“着色”,其中任何两个相连的顶点都不能有相同的颜色。所需的最少核心数就是所需的最少颜色数,这是图的一个属性,称为图的色数。对于这种特殊类型的图,它恰好等于所有相互重叠的任务的最大集合的大小——即“最大危机时刻”。一个始于实际操作系统问题的东西,已经转变为一个关于图的美丽定理[@problem-urlem:3241741]。
现代计算机系统就像一个交响乐团,多层次的管理协同工作。应用程序有其自身的逻辑,操作系统管理单台机器上的资源,而像 Kubernetes 这样的集群编排器则指挥着数百台机器。CPU 核心位于这个复杂层级的核心。
这个乐团中一个常见的头疼问题是等待。当一个核心准备好工作,但它需要的数据却被卡在从慢速硬盘或网络传输的途中时,会发生什么?一个空闲的核心是一种浪费的资源。解决方案是一种巧妙的戏法,称为异步 I/O。其思想是一次性发出多个数据请求,让它们在后台进行。当一个任务在等待其数据到达时,CPU 核心可以切换到另一个数据已经可用的任务。为了确保 CPU 永不空闲,你需要同时有多少个任务“在途”?利用排队论中的一个基本原理——利特尔法则,我们可以推导出确切的数字。它恰好是完美“隐藏”I/O 延迟所需的请求数量,从而保持工作流水线畅通,核心完全饱和。这是一个抽象理论如何被用来调整现实世界应用程序性能的完美例子。
不同控制层之间的相互作用也产生了迷人的行为。操作系统的调度器有其自己的偏好。例如,它可能会尝试将一个任务保留在它之前运行的同一个核心上。这种“软亲和性”是一种温和的建议,旨在将数据保留在该核心的本地缓存中,因为访问本地缓存要快得多。但是,像 Kubernetes 这样的更高级别的系统可能会施加“硬亲和性”规则,使用像 Linux cpusets 这样的机制,将一个容器及其所有线程锁定到一组特定的、不可协商的核心上。当一个运行四个线程的容器最初被分配到四个核心时,一切正常。但是当系统扩容,而同一个容器现在被限制在只有两个核心上时,会发生什么?硬性规则是绝对的。这四个线程现在被困住了,被迫共享这两个核心。操作系统调度器在那个小小的“监狱”里尽力做到公平,平均给每个线程一半核心的时间。每个容器的性能减半,但硬性边界从未被逾越。这种情况在现代云环境中很常见,它说明了系统设计中指导方针与法规之间的关键区别。
为了让这首交响乐更加丰富,并非所有核心都是生而平等的。你的计算机可能包含通用 CPU 核心和高度专业化的图形处理单元 (GPU) 核心。GPU 就像天才,在图形或机器学习等特定、高度并行的任务上快得令人难以置信,但灵活性不如 CPU。当你有一个像视频编码这样的工作负载,其中一部分可以由 GPU 加速,而另一部分不能时,你就面临一个优化问题。你应该将多大比例的工作卸载到 GPU?发送太少的工作会浪费强大的 GPU;发送太多会使它们不堪重负并造成瓶颈。最优策略是找到完美的分割点,即比例 ,它能平衡负载,使得 CPU 和 GPU 都在其全部潜力下工作。这将系统变成一个平衡的流水线,从而最大化整体吞吐量。
也许大规模多核系统最令人敬畏的用途是在科学模拟中。我们在计算机内部构建虚拟宇宙,以理解从蛋白质折叠到星系碰撞的一切。在这里,我们学到了关于我们计算工人的力量与极限的最后、也是最深刻的教训。
第一课是被称为阿姆达尔定律的一剂现实主义良药。你不可能通过雇佣九个女人来在一个月内生出一个孩子。任何任务的某些部分都是天生串行的。在一个并行程序中,这个串行部分限制了可实现的最大加速比。无论你为问题投入多少核心,总时间永远不会少于完成那个单人串行部分所需的时间。此外,当你增加更多核心时,它们通常需要相互通信,这会产生一个随团队规模增大的开销。一个现实的性能模型显示,在某个点之后,增加更多核心会产生递减的回报,甚至最终可能因为核心花费更多时间在通信而非工作上而使程序变慢。
当然,有些问题是并行的完美匹配。如果你需要在计算生物学实验室分析 300 张独立的活检图像,你可以简单地将每张图像分给 100 个核心中的一个,运行三次,就完成了。这被称为“易并行”问题。理想的加速比似乎显而易见。但接着现实介入了。如果所有 100 个核心在同一时刻都试图从同一个共享存储系统读取它们的大图像文件会怎样?结果是数字交通堵塞。存储系统成为 I/O 瓶颈,核心大部分时间都在空闲,等待它们的数据。整体性能不是由计算速度限制,而是由数据传输速度限制。这是高性能计算领域中一个通过艰辛换来的关键教训。
这把我们带到了最后一点,这一点与其说是关于计算机科学,不如说是关于科学哲学。想象一下,你是一名计算化学家,拥有一百万 CPU 小时的预算,任务是理解一个 1000 个原子的蛋白质如何摆动和折叠。你有两个选择。方案 A:使用一个快速、近似的“经典”模型来运行一次非常长的模拟,希望捕捉到蛋白质在微秒尺度上的缓慢、罕见的运动。方案 B:使用一个缓慢、高度精确的“量子”模型 (DFT) 来对蛋白质的小片段进行数千次微小的、独立的计算。
方案 B 是“易并行”的,在墙上时钟时间上会快得多。但对于所提出的问题,它在科学上是无用的。蛋白质是一个协作的、多体系统;其全局运动源于其所有部分的微妙相互作用。研究孤立的片段无法告诉你任何关于这些集体动力学的信息。方案 A 虽然运行得慢一些,但它是唯一能产生整个系统时间连续轨迹的方法,而这是观察客户想要看到的现象的唯一途径。这个教训是深刻的:选择正确的物理模型远比其并行化效率重要无数倍。如果你将百万个核心对准了错误的宇宙,那么它们就毫无价值。CPU 核心的终极应用是作为科学探究的工具,而使用任何工具的第一条规则是理解它适合哪项工作。
从一个简单的资源分配谜题到科学建模的深层哲学问题,CPU 核心的旅程反映了技术本身的旅程。它不仅仅是关于制造更快的计算器,而是关于学习如何组织、编排和部署它们,以解决日益复杂和美好的问题。