try ai
科普
编辑
分享
反馈
  • big.LITTLE 架构

big.LITTLE 架构

SciencePedia玻尔百科
核心要点
  • big.LITTLE 架构在一块芯片上结合了高性能的“大”核和高能效的“小”核,以解决现代计算中速度与能耗之间的权衡问题。
  • 操作系统(OS)调度器对于在异构环境中动态分配任务、管理成本高昂的核心迁移以及确保系统公平性至关重要。
  • 这种设计从根本上影响了整个计算栈,修正了像阿姆达尔定律这样的性能模型,并为编译器和操作系统理念创造了新的机遇。

引言

在你的掌中,智能手机于强大性能和卓越效率之间上演着一场复杂的舞蹈,而实现这一壮举的设计理念便是 ​​big.LITTLE 架构​​。多年来,芯片设计师只需将晶体管做得更小即可获得性能提升,但那个时代已经终结,并催生了“暗硅”问题——我们再也无法同时为芯片的所有部分供电。这一限制催生了一种革命性的折中方案:如果一种核心无法同时做到既快又省电,为何不使用两种专门的核心呢?本文将深入探讨这一重新定义了现代计算的范式转换架构。

本文的探讨分为两个主要部分。首先,在“原理与机制”部分,我们将剖析将强大的“大”核与高效的“小”核配对的基本硬件概念。我们将审视性能与能耗之间的关键权衡,并揭示操作系统(OS)调度器用于管理这些不同资源的复杂逻辑。之后,在“应用与跨学科联系”部分,我们将拓宽视野,观察这种硬件设计如何在计算机科学领域掀起波澜,重塑性能定律,为用户界面响应性带来新挑战,并启发操作系统和编译器设计的新哲学。

原理与机制

要真正领会 ​​big.LITTLE 架构​​ 的精妙之处,我们必须从一个已成为现代芯片设计标志性难题的问题谈起,而非硅晶片本身。几十年来,工程师们享受着物理学带来的一份厚礼,即 ​​Dennard 缩放定律​​。该定律本质上指出,随着晶体管变小,其功率密度保持不变。这意味着我们可以在芯片上集成越来越多的晶体管,并让它们运行得更快,而无需担心融化。那是一个黄金时代。但正如所有美好的事物一样,它也有终结之时。大约在 21 世纪中期,这种缩放定律失效了。晶体管变得如此之小,以至于即使在不主动开关时也开始“泄漏”功率。盛宴结束了。

这导致了一个发人深省的现实,即 ​​暗硅​​ (dark silicon) 问题。我们可以制造拥有数十亿晶体管的芯片,却无法在不超出安全热预算的情况下同时为它们全部供电。在任何给定时刻,芯片的很大一部分必须保持“暗”或未通电状态。于是,问题变成了:我们该如何智能地利用这庞大但功耗受限的硅预算?如果我们无法拥有一种既快如闪电又极其高效的核心,或许我们可以拥有两种?这便是 big.LITTLE 架构的哲学种子:一种对折中方案的 brilliantly 拥抱。

性能的两个面孔:速度与效率

big.LITTLE 架构的核心是一种 ​​异构计算​​。它在同一芯片上将两种不同类型的处理器核心配对:一组高性能的“大”核和一组高效率的“小”核。

​​大核​​ 是赛场上的良驹。它们结构复杂,具有深流水线、复杂的转移预测器和大型缓存。它们的设计目标是实现非常低的 ​​每指令周期数 (CPI)​​ 并以高时钟 ​​频率 (fff)​​ 运行。它们提供最高的单线程性能,能迅速处理要求严苛的计算任务。但这种速度是有代价的:它们消耗大量功率。

​​小核​​ 则是马拉松选手。它们更简单、更小,从设计之初就以实现最大能效为目标。它们的 CPI 可能更高,时钟频率可能更低,但每条指令的能耗仅为大核的一小部分。

让我们看看这是如何运作的。处理器执行一个程序所需的时间由经典性能公式给出:

Texec=I×CPIfT_{\text{exec}} = \frac{I \times \text{CPI}}{f}Texec​=fI×CPI​

其中 III 是指令数。想象一个程序,其中一部分代码必须在强大的核心上运行,但其余部分可以被分流。在一个假设的场景中,一个大核和一个小核同时运行,假设大核被分配了 8×1088 \times 10^88×108 条指令,而小核获得了 1.2×1091.2 \times 10^91.2×109 条指令。尽管大核的指令数较少,但其高频率和低 CPI 可能使其在(比如说)0.290.290.29 秒内完成工作。而小核,由于其更大的工作量和较弱的性能,可能需要 1.281.281.28 秒。由于两部分都需要完成,总程序时间是两者中的最大值:1.281.281.28 秒。在这个特定安排中,小核成为了瓶颈,这说明性能现在是一项团队运动,总执行时间由最后一个冲过终点线的选手决定。

交易的艺术:用能量换取时间

当我们不仅考虑时间,还考虑能量时,big.LITTLE 设计的真正美妙之处就显现出来了。这时,操作系统(OS)调度器——这个硅乐团的指挥家——登场,进行智能的权衡。

假设你有一个大型计算任务和一个必须完成的截止日期。你的目标是在满足截止日期的同时,消耗绝对最低的能量。你会如何处理?你的第一直觉应该是使用你拥有的最节能的工具:小核。

让我们想象一个包含 8×1098 \times 10^98×109 条指令的工作负载,必须在 2.22.22.2 秒的截止日期内完成。我们的小核很高效,每条指令只消耗 0.90.90.9 纳焦耳,但它速度不快,以每秒 1.2×1091.2 \times 10^91.2×109 条指令的速度运行。如果我们让它运行整整 2.22.22.2 秒,它可以完成 1.2×109×2.2=2.64×1091.2 \times 10^9 \times 2.2 = 2.64 \times 10^91.2×109×2.2=2.64×109 条指令。这不足以完成任务。我们还有 8×109−2.64×109=5.36×1098 \times 10^9 - 2.64 \times 10^9 = 5.36 \times 10^98×109−2.64×109=5.36×109 条指令的缺口。

我们该怎么办?我们必须召唤重量级选手:大核。这个核心更“渴”,每条指令消耗 1.61.61.6 纳焦耳,但它快如闪电,以每秒 4.8×1094.8 \times 10^94.8×109 条指令的速度运行。我们把剩下的 5.36×1095.36 \times 10^95.36×109 条指令分流给它。它完成这项工作需要 5.36×1094.8×109≈1.12\frac{5.36 \times 10^9}{4.8 \times 10^9} \approx 1.124.8×1095.36×109​≈1.12 秒。由于 1.121.121.12 秒小于我们的 2.22.22.2 秒截止日期,这个调度是有效的!小核在整个期间运行,而大核在这段时间的一部分内并行运行。通过采用这种“小核优先”的策略,并且只在为满足截止日期而绝对必要时才使用大核,我们实现了最低的可能能耗。这个原则是现代智能手机节省电池寿命的基础。

同样的逻辑也适用于管理芯片的功率预算。考虑一个具有 202020 瓦固定功率上限的芯片。一个持续的、低强度的后台任务需要处理。我们应该让它在备用的大核上运行,还是在专门的小核上运行?让它在大核上运行可能会消耗 4.64.64.6 瓦,只剩下 15.415.415.4 瓦给主要的前台应用程序。但如果我们将它分流到超高效的小核上,它可能只消耗区区 0.560.560.56 瓦!这就为运行主应用程序的大核留下了 19.4419.4419.44 瓦的更大功率预算,使它们能够运行得更快,并提供显著更高的整体性能。小核就像一个“节能虹吸管”,吸走低强度工作,从而为最需要功率的核心释放预算。

乐团的指挥家:操作系统调度器

如果没有一个精密的编舞者——​​操作系统(OS)调度器​​,大核与小核之间这种优雅的舞蹈将无法实现。硬件提供了潜力,但操作系统使其成为现实。其主要挑战是在管理这种深刻的异构性的同时,向应用程序呈现一个简单、一致的接口。

对称性的幻象

当你在手机上运行一个应用时,你不会告诉它“这个线程在大核上运行,那个在小核上运行”。你期望系统能正常工作,并且是公平的。操作系统负责创造这种对称性的幻象。如果它只是简单地为进程分配相等的时间片,那么恰好落在小核上的进程所完成的计算工作量将远少于幸运地落在大核上的进程。这显然是不公平的。

为了解决这个问题,操作系统调度器必须是 ​​容量感知 (capacity-aware)​​ 的。它不能只衡量时间;它必须衡量工作量。它为每个进程维护一个“虚拟时钟”,这个时钟不是按秒推进,而是按一个经容量加权的工作量度量推进。在大核上运行一毫秒可能会使进程的虚拟时钟前进 10 个单位,而在小核上同样的毫秒可能只使其前进 3 个单位。调度器的目标是让所有可运行进程的虚拟时钟随时间以大致相同的速率前进。这涉及一套复杂的机制:跟踪每个核心的实时容量(这可能随温度和频率缩放而变化),在核心之间迁移任务以平衡负载,甚至计算其自身工作(如处理中断)所消耗的容量。

迁移的风险

将任务从小核迁移到大核(反之亦然)看似简单,但这是一个充满成本的旅程。首先,有 ​​上下文切换​​ 的直接成本:必须保存即将换出进程的状态,调度器必须运行,然后恢复即将换入进程的状态。在异构系统上,这更为复杂。保存寄存器所需的时间因核心的内存带宽而异,并且调度开销本身在大核与小核上可能需要不同数量的周期。迁移本身需要跨核心通信(处理器间中断),这增加了延迟。

但隐藏的成本往往更大。当一个线程移动到一个新核心时,它是“冷”启动的。它的工作数据集不在该核心的本地缓存中,其虚拟到物理地址的转换也不在 ​​转译后备缓冲器 (TLB)​​ 中。线程在“预热”时会遭受一连串的缓存和 TLB 未命中,从而拖慢其进度。总的端到端迁移延迟可能长达数十微秒,在处理器时间里这简直是永恒。

因为迁移成本高昂,调度器必须小心,不能反应过度。想象一个计算强度快速波动的任务。调度器可能会看到一个活动突发,决定将其迁移到大核,而当迁移完成时,突发已经结束,它又想把它移回去。这种“乒乓效应”浪费的性能可能比获得的还要多。为防止这种情况,调度器使用 ​​迟滞效应 (hysteresis)​​:它们会等待一小段时间,以确保行为变化是持续的,然后再触发昂贵的迁移。

此外,在严格基于优先级的系统中,如果源源不断的高优先级任务让大核持续繁忙,小核上的低优先级线程可能会被无限期地饿死。一个健壮的调度器还必须实现一种“老化”策略。如果一个线程等待时间过长,它的优先级会被暂时提升,使其能够“插队”并在大核上获得运行机会,从而确保公平性并防止饥饿。

超越原始速度:异构性的微妙形式

大核与小核之间的区别比时钟速度和功耗更深。 “性能”是一个多维度的品质,一个真正智能的调度器必须考虑这些更细微的方面。

内存占用很重要

最重要但又最微妙的区别之一可能在于内存子系统。为重型任务设计的大核通常拥有更大、更复杂的缓存和更大的 TLB。​​TLB 覆盖范围​​——即程序在不产生昂贵的 TLB 未命中的情况下可以访问的内存量——在大核上可能要大得多。

考虑四个具有不同内存工作集大小的应用程序:T1T_1T1​ 为 0.80.80.8 MiB,T2T_2T2​ 为 1.21.21.2 MiB,T3T_3T3​ 为 2.52.52.5 MiB,以及 T4T_4T4​ 为 7.07.07.0 MiB。假设小核的 TLB 覆盖范围为 111 MiB,大核为 888 MiB。最优的放置方案不再显而易见。T1T_1T1​ 的工作集完全在小核的 TLB 覆盖范围内,因此它在那里不会有任何 TLB 未命中。T2T_2T2​、T3T_3T3​ 和 T4T_4T4​ 的工作集都大于小核的覆盖范围,并且会频繁遭受扼杀性能的未命中。然而,所有四个应用程序都能舒适地放在大核的覆盖范围内。为了最小化整个系统的 TLB 未命中总数,策略很明确:将工作集最小的应用程序(T1T_1T1​ 和 T2T_2T2​)放在小核上。这“控制”了它们的未命中率(T1T_1T1​ 为零,T2T_2T2​ 为中等),并为那些在小核上会严重受损的内存密集型应用程序(T3T_3T3​ 和 T4T_4T4​)保留了宝贵的大核位置。这是一种超越简单计算需求的“资源匹配”。

统一的语言与翻译的成本

另一个深层次的挑战位于硬件和软件的交汇处:​​应用程序二进制接口 (ABI)​​。这是管理函数如何相互调用、参数如何传递以及使用哪些寄存器的底层契约。如果大核有 32 个寄存器,而小核只有 16 个,会怎么样?如果一个编译为使用所有 32 个寄存器的程序试图迁移到小核上,它将会彻底失败。

为了实现无缝迁移,系统必须采用一个统一的 ABI,只使用“最小公分母”——即两种核心类型上都可用的 16 个寄存器集。这有一个直接后果:由于可用寄存器减少,程序可能需要更频繁地访问堆栈,从而略微降低性能。这也精确地定义了迁移成本:当一个线程移动时,需要保存和恢复的正是这些共同定义的寄存器的状态。

即使是像同步这样的基本操作也必须重新评估。当一个大核上的线程等待一个小核上线程持有的锁时,预期的等待时间很长。在这种情况下,等待的线程 ​​暂停​​ (park)(阻塞并进入休眠)会更节能。相反,如果一个小核正在等待一个大核,等待时间可能很短,一个紧凑的 ​​自旋锁​​ (spinlock)(忙等待)可能更快,并且有更好的能耗-延迟特性。最优策略变得核心感知,不仅取决于你是否在等待,还取决于你在等待谁。

从暗硅的宏大挑战到调用约定和自旋锁策略的细枝末节,big.LITTLE 架构代表了一次范式转变。它证明了这样一个理念:通过拥抱异构性并在硬件和软件之间建立深度的合作关系,我们可以创造出比任何同构设计都更强大、更高效的系统。这是一场物理与逻辑的美丽而复杂的舞蹈,每秒钟在你的掌心上演数十亿次。

应用与跨学科联系

在之前的讨论中,我们打开了机器的内部,审视了 big.LITTLE 架构的齿轮。我们看到了它如何将高性能的“大”核与节能的“小”核相结合并在原理上发挥作用。但要真正欣赏一个想法的美妙之处,我们必须看到它的实际应用。为什么要费尽周折将两种不同类型的引擎构建到一块芯片中?正如我们将看到的,答案不仅仅是为了节省电池寿命,它关乎一种渗透到整个计算世界的新型智能,从性能的基本定律到我们构建软件的哲学本身。

让我们踏上一段旅程,看看这个巧妙的硬件工程如何在不同领域掀起涟漪,迫使我们重新思考旧问题,并为新的、更复杂的解决方案打开大门。这是一个关于智能妥协的故事,其中速度与效率之间持续的拉锯战催生了一场优雅而强大的舞蹈。

阿姆达尔定律的重塑:新的速度规则

几十年来,我们对并行计算极限的理解一直由阿姆达尔定律塑造。在其经典形式中,它给我们一个发人深省的教训:通过增加处理器数量所能获得的加速,最终受限于你程序中顽固的串行部分——即只能在单个核心上运行的部分。该定律通常假设你所有的处理器都是相同的。但当它们不同时会发生什么呢?

big.LITTLE 架构迫使我们更新这个基础定律。想象一个由串行和并行部分混合组成的任务。串行部分必须在一个核心上运行。然而,并行部分可以分散到所有可用的核心上。在传统的多核芯片中,这个并行部分的性能将是单个核心性能的 NNN 倍,其中 NNN 是核心数量。

在 big.LITTLE 系统上,这个等式发生了变化。并行部分的总处理能力不再是一个简单的倍数;它是一个加权和。如果你有 NbN_bNb​ 个大核,每个以相对速度 rbr_brb​ 运行,以及 NlN_lNl​ 个小核,每个以速度 rlr_lrl​ 运行,那么总的并行处理速率变为 Nbrb+NlrlN_b r_b + N_l r_lNb​rb​+Nl​rl​。将此代入阿姆达尔定律,我们得到了一个新的、异构的版本。这不仅仅是一个数学上的调整;这是一个深刻的视角转变。它告诉我们,加速的潜力现在是一个更丰富、多维的景观。我们可以通过调整大核的数量、小核的数量以及它们各自的速度来塑造我们芯片的性能剖面。该定律现在反映了固有的权衡:我们是增加一个功能强大但耗电的大核,还是一个节俭的小核?答案完全取决于我们期望运行的软件的性质。

指挥家:操作系统的任务交响乐

如果 CPU 核心是音乐家,那么操作系统(OS)调度器就是指挥家,决定哪个音乐家在何时演奏乐谱的哪个部分。大核与小核的引入,将指挥家的工作从简单地将任务分配到一个空舞台,转变为做出复杂的艺术和后勤选择。调度器现在必须处理多个、常常是相互冲突的目标。

宏大的平衡术:能量 vs. 性能

这是 big.LITTLE 的核心戏剧。每个到达的任务都给操作系统带来一个选择:是将其调度到大核上以求快速完成,还是调度到小核上以节省能源。这是一个复杂的优化问题。调度器不仅要考虑核心活跃时消耗的动态能量,还要考虑核心忙碌时持续流失的静态泄漏功率。对于一组各有其截止日期的任务,操作系统必须找到一个将它们映射到大核和小核的方案,以确保没有任务错过截止日期,同时最小化总能耗。

但这个决定比那还要微妙。调度器还必须是任务的鉴赏家。考虑一个大部分时间都在等待数据从慢速内存到达的任务。将这种“内存密集型”任务放在高频大核上运行,就像把 F1 赛车的引擎装在一辆堵在高峰期交通中的城市公交车上——你烧了很多油,却没能跑得更快。核心强大的计算能力被浪费了。一个聪明的调度器会识别出这一点,并将此类任务分配给更节能的小核,为那些能够真正施展拳脚的“计算密集型”任务保留大核。

毫秒之争:确保流畅体验

虽然对于一个能放进口袋的设备来说,节省能源至关重要,但我们对其性能的感知主要取决于它的手感。滚动是否流畅?键盘是否即时出现?这些交互受到一个严格的截止日期制约:为了实现流畅的每秒 60 帧,每一帧都必须在 16.716.716.7 毫秒内绘制并准备好。

这给调度器带来了一个高风险的困境。当你触摸屏幕时,一个 UI 线程被唤醒。操作系统应该为了节俭而让它在小核上启动吗?对于“轻量级”交互来说,这没问题。但如果任务结果是“重量级”的呢?当调度器意识到任务正在落后并决定将其迁移到大核时,宝贵的毫秒已经流失。迁移的成本——检测需求、切换上下文、以及在新核心上预热缓存——可能恰好足以让 UI 错过其截止日期,导致明显的“卡顿”或不连贯。

为了避免这种情况,一个复杂的操作系统可能会采用一种更激进的策略:一旦用户交互开始,它就抢先将主 UI 线程固定在一个大核上。这对于轻量级任务可能有些浪费,但它为重量级任务提供了性能保证。这是一种防止延迟的保险策略,将用户体验置于绝对能效之上。

公平的艺术与热度时刻

调度器的工作不止于此。它还必须是一个公平的仲裁者和一个警惕的热量管理者。

想象一个经典的比例份额调度器,旨在根据线程分配的“权重”给予它们相应的处理时间。在具有相同核心的系统上,这很简单。但在 big.LITTLE 系统上,一个线程获得其“公平份额”意味着什么?在大核上 10% 的时间片与在小核上 10% 的时间片是不同的。为了维持公平,操作系统必须适应,也许可以通过创建一个由其运行核心的容量归一化的“有效权重”概念。然后,将哪些线程放置在哪些核心上的选择就变成了一个在整个系统中最小化整体公平性误差的难题。

此外,所有这些活动都会产生热量。智能手机不能热到无法手持,这给芯片施加了一个严格的总功率预算。操作系统必须像电网运营商一样,在各个核心之间分配这个预算。这引出了另一个优美的优化问题:给定一个总功率预算 BBB,应如何在大核和小核之间分配它以最大化总系统性能?这个问题的解,通常通过拉格朗日乘数法等方法找到,揭示了一个非显而易见的最优策略,即根据每种核心类型的固有效率来分配功率。

现实世界的调度器将所有这些考虑因素结合成一个单一的、动态的策略。它们不只是使用简单的规则,而是使用反馈循环。它们监控平滑化的、时间平均的利用率和温度信号来做决策,并采用迟滞效应——一种制度记忆——以避免在状态之间摇摆不定。这使得操作系统能够在任务需求高且芯片凉爽时,优雅地“强化”任务对大核的亲和性,而在设备开始升温时,则优雅地退让并将工作转移到小核上。

超越调度器:软件的新范式

big.LITTLE 架构的影响并未止步于操作系统调度器。它从根本上改变了我们思考编写和运行软件的方式和应有的方式。

编译器的困境

现代编译器是优化的宗师。对于一段给定的代码,它可能会看到两种实现方式:一种是高度并行化、向量化(SIMD)的版本,使用特殊指令一次处理大量数据;另一种是更简单、更传统的标量版本。在同构系统上,选择很简单:挑选更快的那一个。

在 big.LITTLE 系统上,答案是“视情况而定”。大核上强大的向量单元可能使 SIMD 代码成为明显的赢家。但在小核上,其向量单元可能不那么强大或延迟更高,那些特殊指令的开销可能会使简单的标量代码成为更高效的选择。一个具备异构性感知的编译器可以生成两种版本的代码。然后,应用程序可以在运行时查询它当前在何种类型的核心上运行,并选择最合适的代码路径来执行。这将优化推向了一个新的层次,根据底层硬件的具体特性来定制正在执行的指令。

重新定义操作系统本身

也许最深刻的是,big.LITTLE 挑战了传统的操作系统哲学。大多数操作系统的设计旨在隐藏硬件的复杂性,向应用程序呈现一个干净、统一的抽象。但如果应用程序最清楚自己想如何使用这些异构资源呢?

这是外核(exokernel)设计哲学的核心思想。外核的工作不是隐藏硬件,而是安全地暴露它。在 big.LITTLE 的背景下,这意味着提供一个 API,允许应用程序明确请求“一个大核”和“四个小核”。然后,应用程序——或与之链接的专门库,形成一个单核(unikernel)——就可以自由地实现自己的调度策略,精确地决定如何使用这些被授予的资源。它可以选择在大核上运行其关键的串行代码,同时将其后台并行工作分布在小核上,完全绕过主操作系统调度器的通用逻辑。

这将一个特定的硬件设计与计算机科学中一个深刻而长期的争论联系起来:操作系统应该是一个全知的管理者,还是一个极简主义的赋能者?big.LITTLE 架构使这场辩论比以往任何时候都更加切题。

一个统一的原则

从阿姆达尔定律的理论极限到移动操作系统调度器的复杂舞蹈,再到编译器和操作系统的结构本身,big.LITTLE 架构不仅仅是一个巧妙的硬件技巧。它是工程学中一个统一原则的有力例证:拥抱约束和权衡会导致更智能、更专业、最终更有效的设计。它证明了这样一个理念:通过构建承认其必须执行的工作具有多样性的系统,我们可以创造出一个远大于其各部分之和、且远比其各部分更智能的整体。