在过去的十年中,我参与了几个项目,这些项目设计了各种处理器的 ISA(指令集架构)扩展或全新的 ISA(你甚至可以在 RISC-V 规范的致谢中找到我的名字,可以追溯到第一个公开版本)。当我刚开始时,我对什么构成好的 ISA 知之甚少,而且据我所知,这在任何地方都没有正式教授。然而,随着 RISC-V 作为自定义指令集开放基础的兴起,入门门槛已经大大降低,尝试设计部分或全部指令集的人数也呈指数级增长。
指令集是编译器和微架构之间的通用语言。因此,它与编译器中间语言有很多共同之处,Fred Chow 就此主题撰写了一篇优秀的概述。2
程序员在三个层面上看到目标平台的细节
• ABI(应用程序二进制接口)是一组约定,定义了编译器如何使用可见的硬件特性。这可能是单个编译器私有的,也可能是多个可互操作编译器之间共享的约定。
• 架构定义了硬件保证的一切。这是编译器和操作系统实现人员与硬件实现人员之间的合同。该架构包括枚举设备、配置中断等的机制。ISA 是架构的核心部分,它定义了指令的编码和行为以及它们消耗的操作数。
• 微架构是架构的特定实现。理想情况下,程序员不关心微架构的具体细节,但这些细节通常会泄露。例如,缓存行大小可能是一个微架构细节,但它们会影响伪共享,因此可能对性能产生很大影响。如果您关心侧信道,那么您可能会发现微架构非常重要。
约定通常可以存在于 ABI 或 ISA 中。对于这些约定应该位于何处,没有硬性规定,但这里有一些有用的经验法则
• 如果不同的语言想要做不同的事情,那么它应该在 ABI 中。
• 如果软件需要执行特定任务才能利用微架构特性,则该任务应属于 ISA 而不是 ABI。
我之前写过不存在通用处理器这种东西,1 但也不存在通用 ISA 这种东西。ISA 需要能够高效地让编译器将一组源语言翻译成目标代码。它还必须能够高效地在硬件将采用的各种微架构中实现。
为所有可能的源语言设计 ISA 是很困难的。例如,考虑 C、CUDA(Compute Unified Device Architecture,计算统一设备架构)和 Erlang。每种语言都有非常不同的抽象机。C 具有大量的可变状态和一个附加的并发模型,该模型依赖于共享一切、锁定,并且通常是相当少量的线程。Erlang 具有共享无的并发模型,并且可以扩展到非常大量的进程。CUDA 具有复杂的共享模型,该模型与其并行模型紧密耦合。
您可以(根据定义)将任何这些语言编译为任何图灵完备的目标,但这可能不是高效的。如果将 C 代码编译到 GPU(并利用并行性)很容易,那么 CUDA 就不需要存在了。任何语言家族都有一组隐含的假设,这些假设驱动着关于最有效目标的决策。
包括 C 在内的 Algol 系列语言通常具有良好的引用局部性(包括空间局部性和时间局部性),但具有某种程度的随机访问模式。它们有一个单一的堆栈,并且很大一部分内存访问将是对当前堆栈帧的访问。它们在对象中分配内存,这些对象通常相当小,并且大多数对象不在线程之间共享。面向对象的语言通常执行更多的间接分支和更多的指针追踪。数组处理语言和着色语言通常执行大量具有可预测访问模式的内存访问。
如果您没有明确说明您正在优化的源语言的属性,那么您几乎肯定会隐含地植入一些可能实际上并不成立的假设。
同样,从微架构的角度来看,适用于小型嵌入式微控制器的良好 ISA 对于大型超标量乱序处理器或大规模并行加速器来说可能是糟糕的 ISA。32 位 Arm 未能与英特尔在性能方面竞争,以及 x86 未能在低功耗市场取代 Arm,这都是有充分理由的。您想要在不同规模上优化的内容是不同的。
设计能够扩展到非常大和非常小内核的 ISA 是很困难的。Arm 决定分离其 32 位和 64 位 ISA,这意味着它可以在其 64 位 A 配置文件中假设寄存器重命名和推测执行的基线,在其 32 位 M 配置文件中假设有序执行,并调整两者,假设可能的实现子集。RISC-V 旨在从微小的微控制器扩展到大型服务器处理器。这是否可能是一个开放的研究问题(当然,以前没有任何架构成功过)。
一种通用性确实很重要:ISA 是否是一个稳定的契约?这更多的是一个商业问题,而不是技术问题。一个稳定的 ISA 可以进入一个反馈循环,人们购买它是因为它有在其上运行的软件,人们编写软件在它上面运行是因为他们拥有它。摩托罗拉的 68000 系列长期以来受益于此,英特尔的 x86 系列甚至更长。
这带来了成本:在未来的每个产品中,您都将被当前一代中做出的任何设计决策所困扰。当英特尔开始测试早期奔腾原型机的模拟时,它发现许多游戏设计师发现,他们可以通过依赖英特尔 486 微处理器的标志设置行为中的一个错误,从热循环中节省一条指令。这个错误必须成为架构的一部分:如果奔腾不能运行流行的 486 游戏,客户会责怪英特尔,而不是游戏作者。
如果您购买 NVIDIA GPU,您不会获得解释指令集的文件。它和架构的许多其他部分都是秘密的。如果您想为其编写代码并且不想使用 NVIDIA 的工具链,则您应该生成 PTX,这是一种在某种程度上可移植的中间语言,NVIDIA 驱动程序可以使用它。这意味着 NVIDIA 可以在 GPU 版本之间完全更改指令集,而不会破坏您的代码。相比之下,x86 CPU 应该运行原始的 PC DOS(假设它在固件中具有 BIOS 仿真)以及自 1978 年以来为 PC 平台发布的每个操作系统和每件用户空间软件。
这种差异影响了您可以将 ISA 过度拟合到微架构的程度。x86 和 32 位 Arm 都受到了创建时可构建的内容的严重影响。但是,如果您正在设计 GPU 或特定于工作负载的加速器,那么 ISA 可能会在版本之间发生根本性的变化。早期的 AMD GPU 是 VLIW(超长指令字)架构;现代的 GPU 不是,但仍然可以运行为旧设计编写的着色器。
稳定的 ISA 也会影响您可以进行的实验程度。如果您在 x86 或 AArch64 中添加了一条可能无用(或可能难以在未来的微架构中实现)的指令,那么您会发现一些流行的代码片段在某些关键位置使用了它,并且您将被它困扰。如果您在 GPU 或 AI 加速器中执行相同的操作,那么您可以悄悄地在下一代中删除它。
近年来,一种观点逐渐流行起来,即 ISA 并不重要。这种观点很大程度上是对一个显而易见的观察结果的过度简化:在性能方面,微架构比架构更重要。一个简单的有序流水线可能每周期执行大约 0.7 条指令。一个复杂的乱序流水线可能每周期执行五条或更多条指令(每个内核),这使得同一 ISA 的两种实现之间产生了近一个数量级的差异。相比之下,在我参与的大多数项目中,我看到一个平庸的 ISA 和一个好的 ISA 之间在可比的微架构上产生的性能差异不超过 20%。
这个比较的两个部分值得指出。首先,设计一个好的 ISA 比设计一个好的微架构要便宜得多。如今,如果您去找 CPU 供应商说:“我有一种新技术可以产生 20% 的性能提升”,他们可能不会相信您。这种程度的整体加速并非来自单一技术;它来自应用大量不同的、非常仔细的设计。将此置之不理是极其浪费的。
第二个关键点包含在最后的警告中:“在可比的微架构上。” ISA 约束了可能实现的微架构的设计空间。可以向架构中添加一些东西,这些东西可以启用或阻止特定的微架构优化。
例如,考虑一个任意长度的向量扩展,它在内存中使用源操作数和目标操作数进行操作。如果用户编写 a + b * c
(其中所有三个操作数都是大向量),那么流水线实现将希望从所有三个位置加载,执行加法,执行乘法,然后存储结果。如果您必须在中间接收中断,并且您只完成了一半,您该怎么办?您可能会说,“好吧,加法和乘法是幂等的,所以我们可以重新启动,一切都很好”,但这会引入额外的约束。特别是,硬件必须确保目标不与任何源值别名。如果这些值重叠,则简单地重新启动是很困难的。您可以公开报告加法进度的寄存器,但这会阻止流水线操作,因为您无法报告您正在完成加法和乘法的一部分。如果您正在构建 GPU,那么这就不那么重要了,因为通常,您不会在内核中处理中断(如果您正在处理,那么等待几百个周期来刷新所有正在进行的状态是可以的)。
同样的问题也适用于微代码。您必须能够在微代码指令之前或之后立即接收中断。一个简单的微代码引擎会暂停流水线,发出从微代码指令扩展而来的一组指令,然后恢复。在简单的流水线上,这很好(除了对中断延迟的影响),并且可能会为您提供更好的代码密度。在更复杂的流水线上,这会阻止跨微代码的推测执行,并将带来巨大的性能损失。如果您想要微代码和来自高端内核的良好性能,则需要使用更复杂的技术来实现微代码引擎。这反过来又给 ISA 施加了压力:如果您在微代码引擎中投入了大量硅,那么添加新的微代码指令是有意义的。
如果您正在为简单的单发射有序内核设计 ISA,那么您有一组明确的约束。有序内核不太担心数据依赖性;每条指令都在前一条指令的结果可用时运行。只有较大的内核才进行寄存器重命名,因此使用大量的临时变量是可以的。
它们通常关心解码器复杂性。最初的 RISC 架构具有简单的解码器,因为 CISC(复杂指令集计算机)解码器占用了总面积的很大一部分。有序内核可能由几万个门组成,而复杂的解码器很容易使尺寸(以及因此的成本和功耗)翻倍。简单的解码在这种规模上很重要。
小代码也很重要。小型微控制器内核可能小至 10KB 的 SRAM(静态随机存取存储器)。在考虑总面积成本时,编码效率的小幅下降可能会使一切相形见绌:如果您的代码需要多 20% 的 SRAM,那么这可能相当于内核面积翻倍。不幸的是,此约束几乎直接与前一个约束相矛盾。这就是 Thumb-2 和 RISC-V 专注于易于解码的可变长度编码的原因:它们节省了代码大小,而没有显着增加解码器复杂性。
这是一个复杂的权衡,当考虑多种语言时,这种权衡变得更加复杂。例如,Arm 曾在其某些移动内核上短暂支持 Jazelle DBX(直接字节码执行)。这涉及到直接解码 Java 字节码,Java VM(虚拟机)状态映射到特定寄存器。用软件解释器实现的 Java 加法指令至少需要一次加载来读取指令,一个条件分支来找到正确的处理程序,然后再一个条件分支来执行加法。使用 Jazelle,加载通过指令提取发生,加法会将代表 Java 堆栈顶部的两个寄存器相加。这比解释器效率高得多,但性能不如 JIT(即时)编译器,后者可以在 Java 字节码之间进行更多分析。
Jazelle DBX 是一个有趣的案例研究,因为它仅在特定源语言和微架构的背景下才有意义。它没有为不在 Java VM 中运行的语言提供任何好处。到设备拥有超过约 4MB 的 RAM 时,JIT 的性能超过了 Jazelle。但是,在该范围内,它是一个很好的设计选择。
Jazelle DBX 应该提醒人们,为一个尺寸的内核进行的优化对于其他内核来说可能是非常糟糕的选择。
随着内核变得更大,其他因素开始占据主导地位。我们已经看到了 Dennard 缩放定律的终结,但没有看到摩尔定律的终结。每一代仍然以固定价格获得更多的晶体管,但是如果您尝试全部供电,那么您的芯片就会着火(所谓的“暗硅”问题)。这是近年来片上系统 (SoC) 加速器变得流行的部分原因。如果您可以添加硬件来加快特定工作负载的速度,但在其他时间完全关闭电源,那么这对于功耗来说可能是一个巨大的胜利。需要始终供电的组件最有可能成为性能限制因素。
在许多高端内核上,寄存器重命名逻辑通常是最大的功耗消耗者。寄存器重命名是实现推测和乱序执行的原因。重命名寄存器类似于编译器使用的 SSA(静态单赋值)形式。当指令被分派时,会分配一个新的重命名寄存器来保存结果。当另一条指令想要使用该结果时,它会被分派以使用此重命名寄存器。架构寄存器只是映射到 SSA 寄存器的名称。
从定义它的指令进入推测执行的那一刻起,到另一条写入同一重命名寄存器的指令退出推测(即,肯定发生)为止,重命名寄存器会消耗空间。如果临时值在基本块的末尾仍然存活,那么它将继续消耗重命名寄存器。基本块末尾的分支将开始在其他地方推测性地发出指令,但在该分支不再是推测性的并且后续指令已写入寄存器之前,内核可能需要回滚到该分支的所有内容并恢复该值。ISA 可以对遇到此类问题的可能性产生重大影响。
复杂的寻址模式通常最终在大型内核上很有用。AArch64 和 x86-64 都从中受益,T-Head 扩展将其添加到 RISC-V 中。如果您在循环中进行地址计算(例如,迭代数组),那么将其折叠到加载-存储流水线中会带来两个关键好处:首先,无需为中间值分配重命名寄存器;其次,此计算值永远不会在循环迭代中意外存活。额外加法的功耗小于分配新重命名寄存器的功耗。
请注意,对于非常复杂的寻址模式,例如 Arm 上的前增量和后增量寻址模式,情况并非如此,这些模式会更新基址,因此仍然需要重命名寄存器。这些模式仍然在一定程度上获胜,因为将结果转发到加载-存储流水线中的下一阶段比通过重命名逻辑发送结果更便宜(特别是对于前增量)。
一位构建高端 RISC-V 内核的微架构师对 RISC-V C 扩展提出了特别有见地的批评,他观察到该扩展针对的是指令的最小编码,而不是指令的最小数量。对于小型嵌入式内核来说,这是正确的做法,但大型内核具有与每条执行指令相关的许多固定开销。执行更少的指令来完成相同的工作通常是一种胜利。这就是 SIMD(单指令多数据)指令如此流行的原因:固定开销在更大的工作量上摊销。
即使您不使 ALU(算术逻辑单元)达到寄存器的全宽度,并且花费两个周期将每个半部分推入执行流水线,您仍然可以节省大量的簿记开销。SIMD 指令是在可变长度指令集中有效利用更长编码的好方法:对于四条指令的工作量,48 位编码可能仍然可以大大节省代码大小,从而使更密集的编码可用于更频繁的操作。
复杂的指令调度会导致额外的痛苦。即使是中等大小的有序内核也会遭受分支预测错误惩罚。最初的伯克利 RISC 项目分析了 C 编译器的输出,发现平均每七条指令就有一个分支。事实证明,这对于 C/C++ 代码来说是一个非常持久的启发式方法。
使用七级双发射流水线,您可能一次有 14 条指令在飞行中。如果您错误地预测了一个分支,那么其中一半将是错误的,并且需要回滚,从而使您的实际吞吐量仅为理论吞吐量的一半。现代高端内核通常有大约 200 条飞行中指令——那是超过 28 个基本块,因此 95% 的分支预测器准确率给出的正确预测每个正在执行的分支的概率小于 24%。大型内核真的喜欢任何可以降低错误预测惩罚成本的东西。
32 位 Arm ISA 允许任何指令被谓词化(根据条件代码寄存器中的值有条件地执行)。这对于中小型有序内核来说非常棒,因为它们可以避免分支,但是对于大型内核来说,使一切都谓词化的复杂性很高。谓词化消耗的编码空间很大。对于 AArch64,Arm 考虑完全消除谓词执行,但条件移动和一些其他条件指令提供了如此大的性能提升,以至于 Arm 保留了它们。
Bjarne Stroustrup 说:“语言只有两种:人们抱怨的语言和没人使用的语言。” 这对于指令集(大多数人会遇到的最低级编程语言)来说,就像对于更高级的语言一样适用。好的指令集始终是妥协。
例如,考虑 RISC-V 中的跳转和链接指令。这些指令允许您将任意寄存器指定为链接寄存器。RISC-V 有 32 个寄存器,因此指定一个寄存器需要 32 位指令中的完整五位操作数。RISC-V 跳转和链接指令占用了总共 32 位编码空间的近 1%。据我所知,RISC-V 在此决策上是独一无二的。
Arm、MIPS 和 PowerPC 都具有指定的链接寄存器,其分支和链接指令使用该寄存器。因此,它们需要一位来区分跳转和链接与普通跳转。RISC-V 选择避免将 ABI 烘焙到 ISA 中,但结果是,此指令需要 16 倍的编码空间。
这个决定甚至更糟糕,因为 ABI 泄露到微架构中,而不是架构中。RISC-V 没有专用的返回指令,但是实现通常会(ISA 规范指出这是一个好主意)将 ABI 定义的链接寄存器的跳转寄存器指令视为返回。这意味着使用 ABI 中定义的链接寄存器以外的任何链接寄存器都可能导致分支预测错误。结果是处理将 ABI 烘焙到 ISA 中的所有缺点,但没有享受到任何好处。
这种推理更强烈地适用于堆栈指针。AArch64 和 x86 都具有用于操作堆栈的特殊指令。在来自类 C 语言的大多数代码中,堆栈指针仅在函数序言和尾声中修改,但是存在许多相对于它的加载和存储。这具有在编码中进行优化的潜力,这可以导致微架构中的进一步优化。例如,现代 x86 芯片累积 push 和 pop 指令的堆栈指针偏移量,将它们作为偏移量发出到包含堆栈指针的重命名寄存器(因此它们是独立的并且可以并行发出),然后在最后对堆栈指针进行单次更新。
即使堆栈指针只是 ABI 约定,这种优化也是可能的,但这又是一个 ABI 和微架构共享的约定,那么为什么不利用它来提高 ISA 中的编码效率呢?
最后,大型内核非常关心并行解码。例如,Apple 的 M2 从固定宽度 ISA 中获益匪浅,因为它可以在并行解码指令块。x86 指令集在另一个极端,它需要更多的是解析器而不是解码器。每条指令都在 1 到 15 个字节之间,其中可能包括许多前缀。高端 x86 芯片缓存解码后的指令(尤其是在热循环中),但这会消耗可以用于执行的功耗和面积。
这不一定是一个坏主意。与小型内核和指令密度一样,可变长度指令编码可能允许更小的指令缓存,并且这种节省可能会抵消复杂解码器的成本。
尽管 RISC-V 使用可变长度编码,但确定长度非常便宜。这使得构建一个额外的流水线阶段成为可能,该阶段读取单词块并将一组指令转发到真正的解码器。这远没有解码 x86 那么复杂。
新的 ISA 通常需要很长时间才能获得广泛采用。引导软件生态系统的最简单方法是成为良好的仿真目标。高效仿真 x86 是 AArch64 和 PowerPC 的明确设计目标,正是出于这个原因(尽管 AArch64 在其设计中具有过去几十年二进制翻译研究的优势)。Apple 的 Rosetta 2 设法将大多数 x86-64 指令翻译成一两条 AArch64 指令。
它的几个特性使 AArch64(尤其是 Apple 对它的略微变体)适合快速轻量级的 x86-64 仿真。第一个是拥有更多的寄存器,这允许将所有 x86-64 状态存储在寄存器中。其次,Apple 采用选择加入的 TSO(总存储顺序)模型,这使得内存模型与 x86 相同。(RISC-V 也将其作为一个选项,尽管我不知道有任何扩展允许在宽松内存模型和 TSO 之间动态切换,就像 Apple 的硬件允许的那样。)
如果没有此模式,您要么需要可以提供相关屏障的所有加载和存储的变体,要么需要在所有加载和存储周围插入显式栅栏。前者消耗了大量的编码空间(加载和存储构成了 AArch64 上编码空间的最大单一消费者),后者则需要更多的指令。
在 TSO 之后,从模拟器的角度来看,标志是 x86 的第二大烦人特性。许多 x86 指令设置标志。Mac 的 Virtual PC(PowerPC 上的 x86)在动态避免设置标志方面付出了很多努力,如果没有任何东西使用它们(例如,如果两个标志设置指令背靠背)。
QEMU 做了类似的事情,保留了设置标志的操作的源操作数和操作码,并且仅在某些东西检查标志的值时才计算标志。AArch64 具有与 x86 类似的标志集,因此可以将标志设置指令转换为一两条指令。Arm 在 ISA 的第一个版本中没有正确地做到这一点(从模拟的角度来看)。微软和 Apple(两家发布在 Arm 上运行的操作系统并需要运行大量旧 x86 代码的公司)都提供了反馈,ARMv8.4-CondM 和 ARMv8.5-CondM 添加了额外的模式和指令,用于以不同的方式设置这些标志。Apple 更进一步,使用一个扩展,该扩展在标志寄存器的一些未使用位中设置 x86 中存在但 Arm 中不存在的两个标志,在需要时可以将它们提取并移动到其他标志位中。
RISC-V 决定不使用条件代码。对于微架构师来说,这些代码一直是一个令人讨厌的功能——原因有几个。在最坏的情况下(并且,不知何故,最坏的情况总是 x86),指令可以设置一些标志。就 x86 而言,这尤其令人痛苦,因为进位标志和中断禁用标志在同一个字中(这导致了一些非常有趣的操作系统错误,因为 ABI 声明标志寄存器不会跨调用保留,因此在内核中调用一个函数来禁用中断之后,编译器会非常有帮助地重新启用它们以恢复标志)。
任何更新寄存器一部分的操作都是痛苦的,因为这意味着分配一个新的重命名寄存器,然后从旧值执行掩码更新。即使没有那样,条件代码也意味着许多指令会更新多个寄存器。
Arm,即使在 AArch32 时代,也通过拥有设置标志的指令变体,并且不对大多数操作设置标志,从而使这种情况变得不那么痛苦。RISC-V 决定避免这种情况,而是将比较折叠到分支中,并具有将寄存器设置为值(通常为 1 或 0)的指令,然后可以将该值与比较和分支指令(例如,如果 [不] 相等则分支)一起使用,后者可以与寄存器零一起使用,以表示如果 [不] 为零则分支。
由于此选择,在 RISC-V 上快速模拟 x86-64 可能会困难得多。
避免标志也会对编码密度产生一些有趣的影响。在 C/C++ 代码中,零条件分支非常常见,用于检查参数是否为空。在 x86-64 上,这是通过 testq
(三字节)指令完成的,后跟 je
(如果测试设置了相等条件标志则跳转),这是一个两字节指令。这会产生先前提到过的为标志分配新重命名寄存器的所有烦恼,包括标志寄存器在下一个标志设置指令退出推测之前保持存活的事实。
避免条件代码的决定也使得添加其他谓词操作变得更加困难。Arm 条件选择和递增指令乍一看很奇怪,但使用它在某些压缩基准测试中提供了超过 10% 的加速。这是 AArch64 中一条中等大小的指令:三个寄存器和一个四位字段,指示要测试的条件。这意味着它在操作数空间中消耗 19 位。等效的 RISC-V 指令要么需要额外的源寄存器和用于执行比较的变体,要么需要单源操作数,但需要比较指令来首先将该寄存器设置为零或非零。
2015年,我指导了一名本科生,他对一个按序RISC-V内核进行了扩展,加入了条件移动指令,并扩展了LLVM后端以利用它。他的结论是,对于简单的按序流水线,条件移动指令在许多基准测试中带来了20%的性能提升,在任何基准测试中都没有性能下降,并且只有极小的面积开销。或者,从相反的方向审视结果,在不使用条件移动指令的情况下达到相同的性能,则需要大约四倍的分支预测器状态。
我被告知,这个结果反映了Arm在设计AArch64时,对更大更宽的流水线进行的分析(尽管没有公开)。显然,这是每个经验丰富的CPU设计师都知道,但没有人费心写下来的结果之一。
AArch64 移除了几乎所有的谓词执行 (predication),但保留了一些相对于微架构复杂性而言收益不成比例地高的指令。RISC-V 决定省略条件移动指令主要是基于 Alpha 架构作者的一篇论文,他们后悔添加了条件移动指令,因为它需要在他们的寄存器文件中增加一个额外的读取端口。这是因为条件移动指令必须写回参数或原始值。
这个论点有趣的地方在于,它只适用于极其狭窄的一组微架构。任何小到不需要转发的架构都不需要读取旧值;它只是不写回值。任何进行寄存器重命名的架构都可以将条件移动指令折叠到寄存器重命名逻辑中,几乎免费获得它。Alpha 架构恰好处于两者之间的狭窄地带。
基于特定规模的实现来直观地了解是什么使 ISA 变得快速或缓慢非常容易。但这些直觉可能很快就会出错(或者如果你在完全不同的规模或不同的问题空间中工作,一开始就是错误的)。新技术,例如 NVIDIA Project Denver 和 Apple M 系列芯片可以将一个指令的输出转发到同一束中的另一个指令的方式,可能会对性能产生重大影响,并改变不同 ISA 决策的影响。你的 ISA 是否鼓励编译器生成可以被新技术加速的代码?
如果你在五到十年后回来看这篇文章,请记住技术是进步的。我在这里提出的任何建议都可能已被新技术证伪。如果你有一个好主意,请在不同微架构的模拟上进行衡量,看看它是否有所不同。
1. Chisnall, D. 2014. 世上不存在通用处理器 (There's no such thing as a general-purpose processor). acmqueue 12(10); https://queue.org.cn/detail.cfm?id=2687011.
2. Chow, F. 2013. 跨语言互操作性的挑战 (The challenge of cross-language interoperability). acmqueue 11(10); https://queue.org.cn/detail.cfm?id=2544374.
David Chisnall 是 SCI Semiconductor 的系统架构主管,在那里他领导 CHERIoT 平台在硬件和软件方面的演进。他曾是微软的首席研究员,在那里他参与了各种 ISA 设计项目,包括全新架构和 64 位 Arm 架构的扩展。他还是剑桥大学的访问研究员。他的职业生涯横跨操作系统和编译器以及硬件。他曾两次当选 FreeBSD 核心团队成员,并且是《The Definitive Guide to the Xen Hypervisor》的作者。自 2008 年以来,他一直是 LLVM 的贡献者,并在剑桥大学教授硕士编译器课程。
版权 © 2023 由所有者/作者持有。出版权已授权给 。
最初发表于 Queue 第 21 卷,第 6 期—
在 数字图书馆 中评论这篇文章
Gabriel Falcao, João Dinis Ferreira - 是否采用 PiM (To PiM or Not to PiM)
随着人工智能成为数十亿边缘 IoT(物联网)设备的普及工具,数据移动瓶颈对这些系统的性能和自主性施加了严格的限制。PiM(内存内处理,processing-in-memory)正在兴起,成为一种缓解数据移动瓶颈的方法,同时满足依赖 CNN(卷积神经网络,convolutional neural networks)的边缘成像应用对性能、能源效率和准确性的严格要求。
Mohamed Zahran - 异构计算:大势所趋 (Heterogeneous Computing: Here to Stay)
“异构计算”这个流行语在过去几年中被提及的次数一直在增加,并且在未来几年还会继续听到,因为异构计算已是大势所趋。什么是异构计算?为什么它会成为常态?我们如何从软件和硬件两方面来应对它?本文旨在解答其中一些问题,并就其他问题提出不同的观点。
David Chisnall - 世上不存在通用处理器 (There’s No Such Thing as a General-purpose Processor)
计算机架构中,将处理器和加速器归类为“通用”的趋势日益增长。在今年国际计算机体系结构研讨会 (ISCA 2014) 上发表的论文中,45 篇中有 9 篇明确提到了通用处理器;一篇额外提到了通用 FPGA(现场可编程门阵列,field-programmable gate arrays),另一篇提到了通用 MIMD(多指令多数据流,multiple instruction, multiple data)超级计算机,将定义的范围扩展到了极限。本文提出了一种观点,即根本不存在真正的通用处理器,并且对这种设备的信念是有害的。
Satnam Singh - 无处理器计算 (Computing without Processors)
从程序员的角度来看,硬件和软件之间的区别正在变得模糊。随着程序员努力满足当今系统的性能要求,他们将面临越来越大的需求,需要利用替代计算元件,例如 GPU(图形处理单元,graphics processing units),它是被改造用于数据并行计算的显卡,以及 FPGA(现场可编程门阵列,field-programmable gate arrays),或软硬件。