risc-v会被禁止吗?关于RISC-V的一些误解
risc-v会被禁止吗?关于RISC-V的一些误解int a = xs[i];// C/C 代码因此,ARM 有许多指令,每条指令都做了很多工作。ARM 具有复杂寻址模式的指令以及条件执行指令(仅限 32 位 ARM)。如果条件为真,则执行这些指令,从而避免分支。这两种方法都有优点。在批评一种设计时,弄清事实是很有用的。对于 RISC-V,我想澄清很多误解。我将尝试介绍我遇到的一些备受关注的误解。RISC-V 指令通常比 ARM 指令少很多。ARM 有一条指令LDR用于将数据从内存加载到寄存器中。它旨在能够处理如下典型的 C/C 代码:
来源:内容由半导体行业观察(ID:icbank)编译自medium,谢谢。
RISC-V 是一种用于微处理器的指令集架构 (ISA),人们对其的喜欢或讨厌处于两个极端。特别是因为 ARM 和 RISC-V 阵营之间似乎存在一些竞争。
这不无道理。RISC-V 和 ARM 代表了完全不同的关于如何设计 RISC 芯片的理念。RISC-V 有一个非常长远的观点,强调简单性,而不选择具有短期利益但可能导致长期问题的方案。RISC-V 真正接受了 RISC 的哲学,它让事情变得非常简单,不仅有一个最小的指令集,而且一个以简单指令为主的指令集。
ARM 更像是一个无情的务实的设计选择。就我们目前在硬件方面的能力而言,选择是基于今天和不久的将来有意义的。如果认为这些指令可以提高整体性能,ARM 设计不会回避添加相当复杂的指令。
因此,ARM 有许多指令,每条指令都做了很多工作。ARM 具有复杂寻址模式的指令以及条件执行指令(仅限 32 位 ARM)。如果条件为真,则执行这些指令,从而避免分支。
这两种方法都有优点。在批评一种设计时,弄清事实是很有用的。对于 RISC-V,我想澄清很多误解。我将尝试介绍我遇到的一些备受关注的误解。
误解 1:RISC-V 指令集膨胀(Bloats )程序RISC-V 指令通常比 ARM 指令少很多。ARM 有一条指令LDR用于将数据从内存加载到寄存器中。它旨在能够处理如下典型的 C/C 代码:
// C/C 代码
int a = xs[i];
我们想i从数组xs中提取索引处的数据。我们可以将其转换为 ARM 代码,其中 registerx1保存数组xs的起始地址,而 registerx2保存索引i。我们必须计算从基址开始的字节偏移量x1。对于 32 位整数数组,这意味着将索引乘以i4 以获得字节偏移量。在 ARM 上,我们通过将x2寄存器向左移动两次来实现相同的效果。
x1 ← mem[x1 x2<<2]
mem指主存。我们使用x1 x2和 2、4 或 8 之类的移位来构造源地址。实际的指令会这样写:
; ARM 64-bit code
LDR x1 [x1 x2 lsl #2] ; x1 ← mem[x1 x2<<2]在 RISC-V 中,等效的指令需要 3 条不同的指令(# 标记注释):
# RISC-V code
SLLI x2 x2 2 # x2 ← x2 << 2
ADD x1 x1 x2 # x1 ← x1 x2
LW x1 x1 0 # x1 ← mem[x1 0]对于具有 3 倍密集代码的 ARM 而言,这似乎是一个巨大的收益。您对缓存的压力更小,指令流水线的吞吐量更大。
- 压缩指令救援
除了 RISC-V 仅通过向芯片添加 400 个逻辑门(AND、OR、NAND)来支持压缩指令。这样,两个更常见的指令可以放入一个 32 位字中。最重要的是压缩不会增加延迟。它不像 zip 解压缩。解压缩是常规指令解码的一部分,因此它是即时的。
# RISC-V codeC.SLLI x2 2 # x2 ← x2 << 2
C.ADD x1 x2 # x1 ← x1 x2
C.LW x1 x1 0 # x1 ← mem[x1 0]压缩指令遵循80/20 规则:80% 的时间使用了 20% 的指令。
不要从字面上去理解这句话。这只是一种说法,RISC-V 指令的一小部分被大量使用。RISC-V 设计人员仔细评估了哪些,并将它们中的许多作为压缩指令集的一部分。
有了这个技巧,我们很容易在代码密度上超越 ARM。
实际上,现代 ARM 芯片可以在两种模式下运行,一种是用于向后兼容的 32 位模式 (AArch32),另一种是 64 位模式 (AArch64)。在 32 位模式下,ARM 芯片支持称为 Thumb 的压缩指令。
澄清一下:不要将 64 位模式与指令长度混淆。64 位模式下的指令在 ARM 上仍然是 32 位宽。64 位 ARM 的重点是能够使用 64 位通用寄存器。对于 64 位模式,ARM 完全重新设计了指令集,这就是为什么我们必须经常清楚我们在谈论哪个指令集。
对于 32 位指令集 (RV32I) 和 64 位指令集 (RV64I) 几乎相同的 RISC-V,这种区别不太重要。这是因为 RISC-V 设计人员在设计 RISC-V ISA 时考虑了 32 位、64 位甚至 128 位架构。
无论如何,回到 RISC-V 压缩指令。
虽然为指令消耗更少的内存对缓存很好,但它并不能解决所有问题。我们仍然有更多的指令来解码、执行和写回结果。除了这个问题可以通过宏操作融合来解决。
- 使用宏操作融合(Macro-op Fusion)减少指令数量
宏操作融合,可以将多条指令合二为一。RISC-V 设计人员正在确定编译器应该发出的代码模式,以帮助 RISC-V 硬件设计人员实现宏融合。规则之一是每个操作的目标寄存器是相同的。因为ADD和LW都将它们的结果存储在寄存器x1中,我们可以将它们融合到指令解码器中的一个操作中。
该规则还避免了我读到的反对宏操作融合的常见反对意见之一:在融合指令中维护多个寄存器写入的结果会变得一团糟。不,不是因为每条指令都必须破坏相同的目标寄存器才能被允许融合。因此,融合指令不需要写入多个寄存器。
- 宏操作融合的注意事项
宏操作融合不是免费的午餐。您需要在解码器中添加更多晶体管才能使其正常工作。您还必须与编译器编写者合作,以确保他们生成代码模式,这有助于创建可以融合的指令模式。
RISC-V 的人目前实际上正在积极这样做。然后当然有时它根本不起作用,因为可能存在诸如缓存线之类的边界。然而,对于可以任意长的 x86 指令,这也是一个问题。
误解 2:可变长度指令使并行指令解码复杂化在 x86 世界中,一条指令原则上可以是无限长的,尽管出于实际目的,它的上限为 15 个字节。这使并行解码多条指令的超标量处理器的设计变得复杂。为什么?因为当你拿到 32 字节的代码时,你不知道每条单独的指令从哪里开始。解决这个问题需要使用复杂的技术,这通常需要更多的周期来解码。
有些人将英特尔和 AMD 使用的技术描述为一种试错蛮力技术,您只需在指令开始和结束的位置进行大量猜测。
然而,相比之下,RISC-V 压缩指令增加的复杂性是微不足道的。让我解释一下:获取的指令将始终是 16 位对齐的。这意味着每个 16 位块要么是 16/32 位指令的开始,要么是 32 位指令的结束。
人们可以使用这个简单的事实来设计并行解码 RISC-V 指令的不同方法。
现在,我不是芯片设计师,但我可以想出某种方案来实现这一点。例如,我可以将解码器与每个 16 位块相关联,如下图所示。
然后每个解码器将从其关联的 16 位块中获取其第一个输入。所以解码器D2的第一部分指令将来自指令块B2。如果我们有 32 位指令,则第二部分来自B3 。
当您解码一条指令时,解码器可以决定它是在处理 16 位还是 32 位指令,从而决定是否使用来自下一个块的第二个输入流。这里的重要观察是每个解码器都可以决定使用第二个 16 位指令数据块,或者不独立于所有其他解码器。这意味着您可以并行执行此操作。
相比之下,我不知道如何解码 x86 指令,它可以是 1-15 字节长。这让我觉得这是一个非常难以思考的问题。
不管怎样,RISC-V 的设计者自己都说这不是一个很难解决的问题。我在这里所做的只是试图说明为什么用外行的话来说这并不难。不要太拘泥于我提出的解决方案。它显然缺少很多细节。例如,您需要一些逻辑来切换多路复用器。
误区三:没有条件执行是一个错误32 位 ARM(ARMv7 和更早的架构)具有我们所说的条件指令。采用正常指令,例如LDR加载寄存器或ADD并附加条件,例如EQ等于或NE不等于,您会得到条件指令,例如LDREQand ADDNE。
; ARM 32-bit codeCMP r6 #42
LDREQ r3 #33 ; r3 ← 33 if r6 = 42
LDRNE r3 #12 ; r3 ← 12 if r6 ≠ 42
CMP r3 #8
SUBS r3 r6 #42
ADDEQ r3 #33 ; r3 ← r3 33 if r6 - 42 = 0
ADDNE r3 #12诸如CMP设置条件标志的指令,在条件指令执行时读取。只有在满足条件时才会执行它们。这是一个非常快速的解释。
在之前的文章中,我还解释了为什么 RISC-V 推动者反对条件执行。它使乱序执行 (OoOE) 更难实现,这对于实现高性能芯片来说意义重大。事实上,出于同样的原因,现代 64 位 ARM 处理器(ARMv8 和更高版本)也没有条件执行。它只有条件选择指令(CSEL、CSINC),但这些指令是无条件执行的。
来自 ARM 阵营的反对意见是,如果没有条件指令,您会在更简单的有序微处理器(将使用 32 位 ARM)上降低性能。这里的假设是,在晶体管数量较少的 CPU 上,您将没有足够复杂的分支预测器来避免因分支错误预测而导致的性能损失。
RISC-V 参与者又一次得到了答案。如果您查看SiFive 7 系列内核,他们实际上已经实现了宏操作融合来处理这个问题。只有一条指令的短分支可以融合到一条类似 ARM 的条件指令中。考虑这段代码:
# RISC-V code
BEQ x2 x3 done # jump to done if x2 == x3
ADD x4 x5 x6 # x4 ← x5 x6
done:
SUB x1 x3 x2因为跳转到done只有一条指令,SiFive 7 系列芯片可以识别这种模式并将其融合到一条指令中。在伪代码中,这最终在概念上意味着:
# RISC-V code # if x2 != x3 then x4 ← x5 x6
ADDNE x2 x3 x4 x5 x6
SUB x1 x3 x2此功能称为短前向分支优化。让我强调没有ADDNE指令。这只是说明正在发生的事情的一种方式。这种宏操作融合的好处是我们消除了一个分支。这意味着我们消除了分支预测错误(刷新指令流水线)的潜在成本。SiFive 在专利申请中对此进行了讨论:
例如,可以融合一个或多个指令上的条件分支。在一些实施方式中,条件分支与后续指令融合,使得该组合作为单个非分支指令来执行。
…
融合的微操作可以作为非分支指令执行,从而避免流水线刷新,此外,避免污染分支预测器状态。要融合的宏操作序列可以包括在控制流指令之后的多条指令。
ARM 阵营可以反驳说,依赖宏运算融合是不好的,因为它增加了复杂性和晶体管。然而,ARM 通常使用宏操作融合来融合CMP分支指令,如BEQ或BNE。在 RISC-V 上,条件跳转是用一条指令完成的,大约 15% 的正常代码是分支指令。因此 RISC-V 实际上在这方面有很大的优势。ARM 可以通过使用宏操作融合来抵消这一优势。但是,如果宏操作融合对于 ARM 来说是公平的游戏,那么对于 RISC-V 来说一定是公平的游戏。
误解 4:RISC-V 使用旧的过时向量处理而不是现代 SIMD 指令ARM 阵营中的一些人想要给人输入的印象是 RISC-V 设计人员停留在过去,没有跟上微处理器架构的最新进展。RISC-V 设计人员选择采用矢量处理而不是 SIMD。前者在失宠之前,曾经在旧的 Cray 超级计算机中很流行。SIMD 指令后来被添加到 x86 处理器以帮助多媒体应用程序。
这给人的印象是 SIMD 是一种较新的技术,但事实并非如此。SIMD 指令首次出现在Lincoln TX-2计算机上,该计算机用于实现由Ivan Sutherland创建的名为Sketchpad的第一个 GUI 。
由 Cray 超级计算机执行的矢量处理是一种处理多个数据元素的更先进的方法。这些类型的机器失宠的原因仅仅是超级计算机的出货量更小,以至于普通 PC 由于更大的出货量而超过了它们。
SIMD 指令只是以一种相当随意的方式添加到 x86 处理器中,而没有太多计划在处理多媒体时获得一些简单的速度提升。
SIMD 指令集比用于真正矢量处理的指令集更简单。然而,随着 SIMD 指令集的功能不断扩展,并且它们的长度不断增加,我们最终得到了比向量处理复杂得多的东西。更不用说一些非常混乱和不灵活的东西。
因此,Cray 在 1980 年代使用矢量处理这一事实并不意味着矢量处理是一种过时的技术。这就像说车轮是一种过时的技术,因为它已经存在了很长时间。
相反h矢量处理变得更重要,因为机器学习、高级图形和图像处理能从中受益。这些是我们对性能要求最高的领域。RISC-V 的人并不是唯一意识到这一点的人。ARM 添加了他们自己的向量处理启发指令 SVE2。如果这是一个坏主意,那么每个人都不会在他们的 CPU 中添加这样的指令。
误区五:不需要矢量处理,只用显卡我看到的另一个反对向量处理的论点是,对于更长的向量,你根本不需要 CPU 来处理它。只需将它们发送到现代显卡即可。
然而,这种方法存在几个问题:
1.并非所有 RISC-V 代码都能在配备强大显卡的高端工作站上运行。
2.在 CPU 内存和 GPU 内存之间来回发送数据会导致延迟。
3.显卡并不是真正为通用矢量处理而设计的,即使它们用于通用矢量处理并且与通用 CPU 相比更胜一筹。
我们开始看到带有 AI 加速器的小型微控制器用于执行设备 AI 任务。对于像这样的小型廉价系统,您无法插入一些大型 Nvidia 显卡来处理矢量数据。
管理与图形卡之间的数据发送以进行处理会增加额外的开销和更多的规划需求。除非您有足够的数据要发送过来,否则最好在 CPU 上本地处理数据。
显卡最初设计用于使用顶点着色器和片段着色器处理顶点和片段(像素)等图形数据。显卡逐渐变得更加通用,但这种传统仍然存在。
许多初创公司都希望通过制造自己的专用人工智能加速卡来在人工智能市场挑战英伟达,这并非没有道理。他们看到了一个明显的竞争机会,那就是制造专门用于机器学习计算而不是图形计算的硬件。
具有 SOC-1 芯片的Esperanto Technologies 就是一个例子。他们围绕 RISC-V 处理器内核构建了一个解决方案,以与Nvidia A100 GPU竞争。A100 专门用于加速机器学习计算。然而 A100 有超过 100 个内核(多处理器 SM)。
相比之下,Esperanto Technologies 将 1092 个内核放在一个仅消耗 20 瓦功率的芯片上。相比之下,A100 的功耗为250–300 瓦。
这使您可以在一块板上放置六个 SOC-1 芯片,而仍然只消耗 120 瓦。为了进行相关比较,请考虑常规机架服务器的功耗不能超过 250 瓦。
因此,您可以制作具有 6000 多个 RISC-V 内核的主板,并将它们安装在常规服务器机架中以执行 AI 工作负载。
所有这一切都是通过创建具有矢量处理能力的小型简单 RISC-V 内核来实现的。Esperanto 正在使用这种策略,而不是像 Nvidia 显卡那样制造多处理器内核。
换句话说,您应该只使用显卡进行矢量处理的想法并没有形成主流。精心设计的具有矢量处理能力的 RISC-V 内核将使您能够以更少的功耗在矢量处理方面超越显卡。
这种误解从何而来?它来自于人们认为 CPU 只是大型 x86 风格的内核。向一些大型 x86 内核添加矢量处理不一定有意义。但是谁说你需要制造大的核心?使用 RISC-V,您可以制作任何类型的内核。SOC-1 芯片既有胖的乱序超标量核心,也有具有矢量处理能力的微小有序核心。
误区 6:现代 ISA 必须处理整数溢出当整数算术指令情况溢出经常受到批评时,对 RISC-V 不会导致陷阱(硬件异常)或设置任何标志这一事实的批评。在几乎任何有关RISC-V 的讨论中,似乎都提到了这一点。它被用作 RISC-V 设计人员停留在 1980 年代的一个论点,因为当时没有人检查整数溢出。
这里有很多东西要解压,所以让我们从简单的开始。大多数广泛使用的语言的编译器,在发布模式或默认情况下不会导致溢出。示例包括:
C/C and Objective-C
Rust (not in release)
Go
Java/Kotlin
C# (use checked block)
其中一些甚至在默认情况下也不会在调试中进行。addExact在Java中,如果你想要溢出异常,你需要调用subtractExact例如。在 C# 中,您需要将代码放在所谓的检查上下文中:
try {
checked {
int y = 1000000000;
short x = (short)y;
}
}
catch (OverflowException ex) {
MessageBox.Show("Overflow");
}在 Go 中,您需要使用带有函数的溢出库,例如overflow.Add overflow.Sub或导致恐慌(类似于异常)的变体,例如overflow.Addp和overflow.Subp。
我能找到的唯一一种在整数溢出时抛出异常的主流编译语言是 Swift,如果你用谷歌搜索,你会发现很多人对此不满意。因此,认为这是 RISC-V 没有内置支持的重大失败的想法似乎很奇怪。
许多动态类型语言(如 Python)以在整数溢出到更大类型时立即升级而闻名。然而,这些语言非常慢,以至于 RISC-V 所需的额外指令几乎没有什么不同。
误区 7:整数溢出处理过于臃肿通常在讨论整数溢出时,据称 RISC-V 需要的代码量是其他指令集架构的四倍。然而,这是最坏的情况。只需添加分支指令即可处理大量常见情况。
添加无符号整数时的溢出检查:
add t0 t1 t2 # t0 ← t1 t2
bltu t0 t1 overflow # goto overflow if t0 < t1当一个参数的符号已知时的有符号整数:
addi t0 t1 42 # t0 ← t1 42
blt t0 t1 overflow # goto overflow if t0 < t1有两个符号未知的操作数的一般情况:
add t0 t1 t2 # t0 ← t1 t2
slti t3 t2 0 # t3 ← t2 < 0
slt t4 t0 t1 # t4 ← t0 < t1
bne t3 t4 overflow # goto overflow if t3 ≠ t4误区 8:整数溢出实现起来很便宜在真空中获取和存储溢出位不会花费太多。但这就像说编程中的变异函数在内存或 CPU 周期方面的成本并不高。这是题外话。
在常规编程中提倡纯函数(非变异)的原因之一是因为它们比改变共享状态的更容易并行运行。乱序超标量处理器也是如此。超标量处理器并行运行许多指令。如果指令不共享任何状态,则并行运行指令会更容易。
状态寄存器引入了共享状态,从而引入了指令之间的依赖关系。这就是为什么 RISC-V 被设计为没有任何状态寄存器的原因。例如,RISC-V 中的分支指令直接比较两个寄存器,而不是读取状态寄存器。
为了在超标量处理器中支持乱序评估指令的状态寄存器,您需要更复杂的簿记。这不仅仅是添加几个触发器来存储位的问题。
最后许多对 RISC-V 的批评是基于不了解技术和没有看到更大的展望。RISC-V 如何实现异构计算环境的创建,其内核针对不同类型的计算进行了优化,这些环境协同工作是人们不习惯的。
不管他们应该怎么做,这不是一个新颖的想法。Playstation 3 具有类似的架构,称为Cell,具有称为 Power Processor Element (PPE) 的通用内核和称为 Synergistic Processing Elements (SPE) 的专用矢量处理器。
然而,PPE 和 SPE 具有不同的指令集,使 Cell 架构的工作变得复杂。使用当今的现代 RISC-V 系统,您将受益于每个内核都将具有相同的基本指令集和寄存器。您可以在所有内核上使用相同的工具进行调试和测试。
★ 点击文末【阅读原文】,可查看本篇原文链接!
*免责声明:本文由作者原创。文章内容系作者个人观点,半导体行业观察转载仅为了传达一种不同的观点,不代表半导体行业观察对该观点赞同或支持,如果有任何异议,欢迎联系半导体行业观察。
今天是《半导体行业观察》为您分享的第2986内容,欢迎关注。
晶圆|集成电路|设备|汽车芯片|存储|台积电|AI|封装
原文链接!