5

我在常春藤桥上。jnz我发现内循环和外循环不一致的性能行为。

以下简单程序有一个固定大小为 16 的内部循环:

global _start
_start:
    mov rcx, 100000000
.loop_outer:
    mov rax,    16

.loop_inner:
    dec rax
    jnz .loop_inner

    dec rcx
    jnz .loop_outer

    xor edi, edi
    mov eax, 60
    syscall

perf工具显示外部循环运行 32c/iter。它表明jnz需要 2 个周期才能完成。

然后我在 Agner 的指令表中搜索,条件跳转有 1-2 个“倒数吞吐量”,并带有注释“如果没有跳转则快”。

在这一点上,我开始相信上述行为是可以预料的。但是为什么jnz在外循环中只需要 1 个循环就可以完成呢?

如果我完全删除该.loop_inner部分,外部循环运行 1c/iter。行为看起来不一致。

我在这里缺少什么?

编辑以获取更多信息:

perf上述程序使用命令的结果:

perf stat -ecycles,branches,branch-misses,lsd.uops,uops_issued.any -r4 ./a.out

是:

 3,215,921,579      cycles                                                        ( +-  0.11% )  (79.83%)
 1,701,361,270      branches                                                      ( +-  0.02% )  (80.05%)
        19,212      branch-misses             #    0.00% of all branches          ( +- 17.72% )  (80.09%)
        31,052      lsd.uops                                                      ( +- 76.58% )  (80.09%)
 1,803,009,428      uops_issued.any                                               ( +-  0.08% )  (79.93%)

参考案例的perf结果:

global _start
_start:
    mov rcx, 100000000
.loop_outer:
    mov rax,    16
    dec rcx
    jnz .loop_outer

    xor edi, edi
    mov eax, 60
    syscall

是:

   100,978,250      cycles                                                        ( +-  0.66% )  (75.75%)
   100,606,742      branches                                                      ( +-  0.59% )  (75.74%)
         1,825      branch-misses             #    0.00% of all branches          ( +- 13.15% )  (81.22%)
   199,698,873      lsd.uops                                                      ( +-  0.07% )  (87.87%)
   200,300,606      uops_issued.any                                               ( +-  0.12% )  (79.42%)

所以原因很清楚:LSD 在嵌套情况下由于某种原因停止工作。减小内部循环大小会稍微减轻缓慢,但不能完全减轻。

搜索英特尔“优化手册”,我发现如果循环包含“超过八个已采用的分支”,LSD 将不起作用。这以某种方式解释了这种行为。

4

2 回答 2

3

TL;DR:DSB 似乎只能每隔一个周期提供一次内部循环的跳转。DSB-MITE 开关也占执行时间的 9%。


简介 - 第 1 部分:了解 LSD 性能事件

我将首先讨论IvB 和 SnB 微架构上 LSDLSD.UOPSLSD.CYCLES_ACTIVE性能事件发生的时间以及一些特性。一旦我们建立了这个基础,我们就可以回答这个问题。为此,我们可以使用专门设计用于准确确定这些事件何时发生的小段代码。

根据文档:

LSD.UOPS:LSD 提供的 Uop 数。
LSD.CYCLES_ACTIVE: LSD 提供的 Cycles Uops,但不是来自解码器。

这些定义很有用,但正如您稍后将看到的,它们不够精确,无法回答您的问题。更好地理解这些事件很重要。此处提供的一些信息并未由英特尔记录,这只是我对经验结果和我所经历的一些相关专利的最佳解释。尽管我无法找到描述 SnB 或更高版本微架构中 LSD 实现的具体专利。

以下每个基准测试都以包含基准名称的注释开头。除非另有说明,否则所有数字都在每次迭代中标准化。

; B1
----------------------------------------------------
    mov rax, 100000000
.loop:
    dec rax
    jnz .loop
----------------------------------------------------
Metric                             |  IvB   |  SnB
----------------------------------------------------
cycles                             |  0.90  |  1.00
LSD.UOPS                           |  0.99  |  1.99
LSD.CYCLES_ACTIVE                  |  0.49  |  0.99
CYCLE_ACTIVITY.CYCLES_NO_EXECUTE   |  0.00  |  0.00
UOPS_ISSUED.STALL_CYCLES           |  0.43  |  0.50

循环体中的两条指令都被mac融合到一个uop中。IvB 和 SnB 上只有一个执行端口可以执行跳转指令。因此,最大吞吐量应为 1c/iter。不过,出于某种原因,IvB 的速度要快 10%。

根据执行 uop 计数不是处理器宽度倍数的循环时性能会降低吗?,即使有可用的问题槽,IvB 和 SnB 中的 LSD 也无法跨循环体边界发出微指令。由于循环包含单个 uop,我们希望 LSD 每个周期发出一个 uop,并且LSD.CYCLES_ACTIVE应该大约等于周期总数。

在 IvB 上,LSD.UOPS正如预期的那样。也就是说,LSD 将在每个周期发出一个 uop。请注意,由于循环数等于迭代次数,即等于 uop 的数量,我们可以等效地说 LSD 每次迭代发出一个 uop。本质上,大多数执行的微指令都是从 LSD 发出的。但是,LSD.CYCLES_ACTIVE大约是周期数的一半。这怎么可能?在这种情况下,不应该只从 LSD 发出一半的微指令吗?我认为这里发生的情况是循环基本上展开了两次,每个循环发出两个微指令。尽管如此,每个周期只能执行一个 uop,但RESOURCE_STALLS.RS它为零,表明 RS 永远不会被填满。然而,RESOURCE_STALLS.ANY大约是循环计数的一半。现在把所有这些放在一起,似乎 LSD 实际上每隔一个周期就会发出 2 次微指令,并且每隔一个周期就会达到一些结构限制。CYCLE_ACTIVITY.CYCLES_NO_EXECUTE确认在任何给定周期内 RS 中始终存在至少一个读取 uop。以下实验将揭示展开发生的条件。

在 SnB 上,LSD.UOPS显示从 LSD 发出的微指令总数是两倍。还LSD.CYCLES_ACTIVE表明 LSD 大部分时间都处于活动状态。CYCLE_ACTIVITY.CYCLES_NO_EXECUTEUOPS_ISSUED.STALL_CYCLESIvB 一样。以下实验有助于了解正在发生的事情。似乎测量LSD.CYCLES_ACTIVE值等于真实LSD.CYCLES_ACTIVE+ RESOURCE_STALLS.ANY。因此,要得到实数LSD.CYCLES_ACTIVERESOURCE_STALLS.ANY必须从实测中减去LSD.CYCLES_ACTIVE。这同样适用于LSD.CYCLES_4_UOPS。实数LSD.UOPS可以计算如下:

LSD.UOPS实测=LSD.UOPS实数+ ((LSD.UOPS实测/LSD.CYCLES_ACTIVE实测)* RESOURCE_STALLS.ANY)

因此,

LSD.UOPS实数=LSD.UOPS测量值- ((LSD.UOPS测量值/LSD.CYCLES_ACTIVE测量值) * RESOURCE_STALLS.ANY)
     =LSD.UOPS测量值* (1 - ( RESOURCE_STALLS.ANY/LSD.CYCLES_ACTIVE测量值))

对于我在 SnB 上运行的所有基准(包括此处未显示的),这些调整都是准确的。

请注意,RESOURCE_STALLS.RSSnBRESOURCE_STALLS.ANY上的 和 就像 IvB 一样。因此,就这个特定的基准而言,LSD 在 IvB 和 SnB 上的工作方式似乎相同,只是事件LSD.UOPSLSD.CYCLES_ACTIVE计数不同。

; B2
----------------------------------------------------
    mov rax, 100000000
    mov rbx, 0
.loop:
    dec rbx
    jz .loop
    dec rax
    jnz .loop
----------------------------------------------------
Metric                             |  IvB   |  SnB
----------------------------------------------------
cycles                             |  1.98  |  2.00
LSD.UOPS                           |  1.92  |  3.99
LSD.CYCLES_ACTIVE                  |  0.94  |  1.99
CYCLE_ACTIVITY.CYCLES_NO_EXECUTE   |  0.00  |  0.00
UOPS_ISSUED.STALL_CYCLES           |  1.00  |  1.00

在 B2 中,每次迭代有 2 个微指令,并且都是跳跃。第一个永远不会被取走,所以仍然只有一个循环。我们希望它以 2c/iter 运行,这确实是这样。LSD.UOPS显示大多数 uops 是从 LSD 发出的,但LSD.CYCLES_ACTIVE显示 LSD 只有一半时间处于活动状态。这意味着循环没有展开。因此,似乎只有在循环中有一个 uop 时才会发生展开。

; B3
----------------------------------------------------
    mov rax, 100000000
.loop:
    dec rbx
    dec rax
    jnz .loop
----------------------------------------------------
Metric                             |  IvB   |  SnB
----------------------------------------------------
cycles                             |  0.90  |  1.00
LSD.UOPS                           |  1.99  |  1.99
LSD.CYCLES_ACTIVE                  |  0.99  |  0.99
CYCLE_ACTIVITY.CYCLES_NO_EXECUTE   |  0.00  |  0.00
UOPS_ISSUED.STALL_CYCLES           |  0.00  |  0.00

这里也有2个uop,但第一个是单周期ALU uop,与跳转uop无关。B3 帮助我们回答以下两个问题:

  • 如果跳转的目标不是跳转 uop,那么LSD.UOPSLSD.CYCLES_ACTIVE仍然会在 SnB 上计数两次吗?
  • 如果循环包含 2 个微指令,其中只有一个是跳转,LSD 会展开循环吗?

B3 表明这两个问题的答案都是“否”。

UOPS_ISSUED.STALL_CYCLES表明如果 LSD 在一个周期内发出两个跳转微指令,它只会停止一个周期。这在 B3 从来没有发生过,所以没有摊位。

; B4
----------------------------------------------------
    mov rax, 100000000
.loop:
    add rbx, qword [buf]
    dec rax
    jnz .loop
----------------------------------------------------
Metric                             |  IvB   |  SnB
----------------------------------------------------
cycles                             |  0.90  |  1.00
LSD.UOPS                           |  1.99  |  2.00
LSD.CYCLES_ACTIVE                  |  0.99  |  1.00
CYCLE_ACTIVITY.CYCLES_NO_EXECUTE   |  0.00  |  0.00
UOPS_ISSUED.STALL_CYCLES           |  0.00  |  0.00

B4 有一个额外的转折;它在融合域中包含 2 个微指令,但在融合域中包含 3 个微指令,因为加载 ALU 指令在 RS 中未融合。在之前的基准测试中,没有微融合的微指令,只有宏融合的微指令。这里的目标是了解 LSD 如何处理微融合的微指令。

LSD.UOPS表明 load-ALU 指令的两个 uop 已经消耗了一个 issue slot(融合跳转 uop 只消耗一个 slot)。同样因为LSD.CYCLES_ACTIVE等于cycles,所以没有展开。循环吞吐量符合预期。

; B5
----------------------------------------------------
    mov rax, 100000000
.loop:
    jmp .next
.next:
    dec rax
    jnz .loop
----------------------------------------------------
Metric                             |  IvB   |  SnB
----------------------------------------------------
cycles                             |  2.00  |  2.00
LSD.UOPS                           |  1.91  |  3.99
LSD.CYCLES_ACTIVE                  |  0.96  |  1.99
CYCLE_ACTIVITY.CYCLES_NO_EXECUTE   |  0.00  |  0.00
UOPS_ISSUED.STALL_CYCLES           |  1.00  |  1.00

B5 是我们需要的最后一个基准。它与 B2 类似,因为它包含两个分支微指令。但是,B5 中的一个跳转微分是向前无条件跳转。结果与 B2 相同,表明跳转 uop 是否有条件无关紧要。如果第一个跳转 uop 是有条件的,而第二个不是,这也是这种情况。

简介 - 第 2 部分:LSD 中的分支预测

LSD 是在 uop 队列 (IDQ) 中实现的机制,可以提高性能并降低功耗(因此,减少了热量排放)。它可以提高性能,因为前端存在的一些限制可以在 uop 队列中放宽。特别是在 SnB 和 IvB 上,MITE 和 DSB 路径的最大吞吐量均为 4uops/c,但就字节而言,分别为 16B/c 和 32B/​​c。uop队列带宽也是4uops/c,但对字节数没有限制。只要 LSD 从 uop 队列发出 uop,前端(即获取和解码单元)甚至IDQ 下游不需要的逻辑都可以断电。在 Nehalem 之前,LSD 是在 IQ 单元中实施的. 从 Haswell 开始,LSD 支持包含来自 MSROM 的微指令的循环。Skylake 处理器中的 LSD 被禁用,因为显然它有问题。

循环通常包含至少一个条件分支。LSD 本质上监控后向条件分支并尝试确定构成循环的微指令序列。如果 LSD 花费太多时间来检测环路,则性能可能会下降并且可能会浪费功率。另一方面,如果 LSD 过早锁定一个循环并尝试重放它,则循环的条件跳转实际上可能会失败。这只能在执行条件跳转后检测到,这意味着后面的微指令可能已经发出并分派执行。所有这些微指令都需要刷新,并且需要激活前端才能从正确的路径获取微指令。

我们已经知道 SnB 及以后的分支预测单元 (BPU) 可以在总迭代次数不超过某个小数时正确预测循环的条件分支何时通过,之后 BPU 假定循环将迭代永远。如果 LSD 使用 BPU 的复杂功能来预测锁定循环何时终止,它应该能够正确预测相同的情况。LSD 也有可能使用自己的分支预测器,这可能更简单。让我们来了解一下。

mov rcx, 100000000/(IC+3)
.loop_outer:
    mov rax, IC
    mov rbx, 1 

.loop_inner:
    dec rax
    jnz .loop_inner

    dec rcx
    jnz .loop_outer

OCIC分别表示外部迭代次数和内部迭代次数。这些相关如下:

OC= 100000000/( IC+3) 其中IC> 0

对于任何给定IC的,退役的微指令总数是相同的。此外,融合域中的微指令数等于未融合域中的微指令数。这很好,因为它确实简化了分析,并允许我们在不同的值之间进行公平的性能比较IC

与问题中的代码相比,多了一条指令,mov rbx, 1,因此外循环中的微指令总数正好是 4 微指令。这使我们能够使用LSD.CYCLES_4_UOPSLSD.CYCLES_ACTIVE和之外的性能事件BR_MISP_RETIRED.CONDITIONAL。请注意,由于只有一个分支执行端口,因此每次外循环迭代至少需要 2 个周期(或根据 Agner 的表格,1-2 个周期)。另请参阅:LSD 能否从检测到的循环的下一次迭代中发出 uOP?.

跳转 uops 的总数为:

OC+ IC* OC= 100M/( IC+3) + IC*100M/( IC+3)
     = 100M( IC+1)/( IC+3)

假设最大跳转uop吞吐量为1个周期,最优执行时间为100M( IC+1)/( IC+3)个周期。在 IvB 上,如果我们想要严格,我们可以改为使用 0.9/c 的最大跳转 uop 吞吐量。将其除以内部迭代的数量会很有用:

OPT= (100M( IC+1)/( IC+3)) / (100M IC/( IC+3)) =
    100M( IC+1) * ( IC+3) / ( IC+3) * 100M IC=
    ( IC+1)/ IC= 1 + 1 /IC

因此,OPT对于 > 1,1 < <= 1.5。IC设计 LSD 的人可以使用它来比较 LSD 的不同设计。我们很快也会使用它。换句话说,当循环总数除以跳跃总数为 1(或 IvB 上为 0.9)时,可以获得最佳性能。

假设两个跳跃的预测是独立的,并且jnz .loop_outer很容易预测,那么性能取决于 的预测jnz .loop_inner。在将控制更改为锁定循环之外的微指令的错误预测中,LSD 终止循环并尝试检测另一个循环。LSD 可以表示为具有三种状态的状态机。在一种状态下,LSD 正在寻找循环行为。在第二种状态下,LSD 正在学习循环的边界和迭代次数。在第三种状态下,LSD 正在重放循环。当循环存在时,状态从第三个变为第一个。

正如我们从前一组实验中了解到的,当后端相关的问题停顿时,SnB 上会出现额外的 LSD 事件。所以需要相应地理解这些数字。IC请注意,上一节中没有测试 =1的情况。将在这里讨论。还记得,在 IvB 和 SnB 上,内部循环可能会展开。外部循环永远不会展开,因为它包含多个微指令。顺便说一句,LSD.CYCLES_4_UOPS按预期工作(抱歉,这并不奇怪)。

下图显示了原始结果。我仅在 IvB 和 SnB 上分别显示了高达IC=13 和=9 的结果。IC我将在下一节讨论更大的值会发生什么。请注意,当分母为零时,无法计算该值,因此不会绘制它。

uop 指标 周期指标

LSD.UOPS/100M是从 LSD 发出的 uops 数量与 uops 总数的比率。LSD.UOPS/OC是每次外部迭代从 LSD 发出的平均微指令数。LSD.UOPS/(OC*IC)是每次内部迭代从 LSD 发出的平均微指令数。BR_MISP_RETIRED.CONDITIONAL/OC是每次外部迭代被错误预测的退休条件分支的平均数量,对于 all ,IvB 和 SnB 显然为零IC

对于ICIvB 上的 =1,所有微指令都是从 LSD 发出的。始终不采用内部条件分支。第二张图显示的LSD.CYCLES_4_UOPS/LSD.CYCLES_ACTIVE指标显示,在 LSD 处于活动状态的所有周期中,LSD 每个周期发出 4 条微指令。我们从之前的实验中了解到,当 LSD 在同一个周期内发出 2 个跳转微指令时,由于某些结构限制,它不能在下一个周期发出跳转微指令,因此会停顿。LSD.CYCLES_ACTIVE/cycles表明LSD(几乎)每隔一个周期就会停止。我们预计执行外部迭代大约需要 2 个周期,但cycles表明它需要大约 1.8 个周期。这可能与我们之前看到的 IvB 上的 0.9 跳转 uop 吞吐量有关。

IC除了两件事之外,SnB 上的情况=1 是相似的。首先,外部循环实际上需要 2 个周期,而不是 1.8 个。其次,所有三个 LSD 事件计数都是预期的两倍。它们可以按照上一节中的讨论进行调整。

IC当>1时,分支预测特别有趣。下面详细分析IC=2的情况。LSD.CYCLES_ACTIVELSD.CYCLES_4_UOPS显示在大约 32% 的所有周期中,LSD 处于活动状态,在这些周期中的 50% 中,LSD 每个周期发出 4 个微指令。所以要么是预测错误,要么是 LSD 在循环检测状态或学习状态下花费了很多时间。尽管如此,cycles/( OC* IC) 大约是 1.6,或者换句话说,cycles/jumps为 1.07,接近最优性能。很难从 LSD 中确定哪些 uops 以 4 个一组的形式发布,哪些 uops 以小于 4 个的组从 LSD 发布。事实上,我们不知道在存在 LSD 错误预测的情况下如何计算 LSD 事件。潜在的展开增加了另一个层次的复杂性。LSD 事件计数可以被认为是 LSD 发出的有用微指令的上限以及 LSD 发出有用微指令的周期。

随着IC增加,减少LSD.CYCLES_ACTIVELSD.CYCLES_4_UOPS减少以及性能缓慢但持续下降(请记住cycles/( OC* IC) 应该与 进行比较OPT)。就好像最后一次内部循环迭代被错误预测了,但它的错误预测惩罚随着 增加IC。请注意,BPU 总是正确地预测内循环迭代的次数。


答案

我将讨论 any 会发生什么IC,为什么较大IC的性能会下降,以及性能的上限和下限是什么。本节将使用以下代码:

mov rcx, 100000000/(IC+2)
.loop_outer:
    mov rax, IC

.loop_inner:
    dec rax
    jnz .loop_inner

    dec rcx
    jnz .loop_outer

这与问题中的代码基本相同。唯一的区别是外部迭代的数量被调整以保持相同数量的动态微指令。请注意,LSD.CYCLES_4_UOPS在这种情况下这是无用的,因为 LSD 在任何周期中都不会发出 4 微指令。以下所有数据仅适用于 IvB。不过不用担心,SnB 的不同之处将在文中提及。

在此处输入图像描述

IC=1 时,cycles/jumps 为 0.7(SnB 上为 1.0),甚至低于 0.9。我不知道这个吞吐量是如何实现的。性能随着 值的增大而降低IC,这与 LSD 活动周期的减少有关。当IC=13-27(SnB 上为 9-27)时,LSD 发出零微指令。我认为在这个范围内,LSD 认为由于错误预测最后一次内部迭代而导致的性能影响大于某个阈值,它决定永远不会锁定循环并记住它的决定。当IC<13 时,LSD 似乎具有攻击性,也许它认为循环更可预测。对于IC>27,LSD 活动周期计数缓慢增长,这与性能的逐渐提高相关。虽然图中没有显示,如IC远远超过 64,大多数 uops 将来自 LSD,cycles/jumps 稳定在 0.9。

范围的结果IC=13-27 特别有用。问题停顿周期约为总周期计数的一半,也等于调度停顿周期。正是由于这个原因,内部循环以 2.0c/iter 执行;因为内部循环的跳转微指令每隔一个周期就会发出/调度一次。当 LSD 未激活时,微指令可以来自 DSB、MITE 或 MSROM。我们的循环不需要微码辅助,因此 DSB、MITE 或两者都可能存在限制。我们可以进一步调查以确定使用前端性能事件的限制在哪里。我已经这样做了,结果表明大约 80-90% 的微指令来自 DSB。DSB 本身有很多限制,似乎循环正在触及其中之一。似乎 DSB 需要 2 个周期来提供针对自身的跳转微指令。此外,对于全IC范围内,由于 MITE-DSB 切换导致的停顿占所有周期的 9%。同样,这些开关的原因是由于 DSB 本身的限制。请注意,最多 20% 是从 MITE 路径传递的。假设 uops 不超过 MITE 路径的 16B/c 带宽,我认为如果 DSB 不存在,循环将以 1c/iter 执行。

上图还显示了 BPU 误预测率(每次外循环迭代)。在 IvB 上,IC=1-33 为 0,但IC=21 时为 0-1,IC=34-45时为 0-1, IC>46 时为 1。在 SnB 上,IC=1-33 为零,否则为 1。

于 2019-01-19T02:44:09.417 回答
3

(部分答案/猜测在哈迪发布详细分析之前我没有写完;其中一些从评论中继续)

Agner 的说法“在 uop 缓存不是瓶颈的情况下,循环缓冲区没有可测量的效果……”是错误的吗?因为这肯定是一个可测量的效果,并且 uop 缓存不是瓶颈,因为缓存的容量约为 1.5K。

是的,Agner 将其称为环回缓冲区。他的说法是,在设计中添加 LSD 不会加速任何代码。但是,是的,对于非常紧凑的循环来说,这似乎是错误的,至少对于嵌套循环来说是这样。显然 SnB/IvB 确实需要循环缓冲区来发出或执行 1c/iter 循环。除非微架构瓶颈是在分支后从 uop 缓存中获取 uop,在这种情况下,他的警告涵盖了这一点。

除了 uop-cache 未命中之外,在某些情况下读取 uop 缓存可能会成为瓶颈。例如,如果 uops 由于对齐效应而没有很好地打包,或者如果它们使用大的立即数和/或需要额外周期才能从 uop 缓存中读取的位移。有关这些效果的更多详细信息,请参阅Agner Fog 的 uarch 指南的 Sandybridge 部分。您认为容量(如果包装完美,最高可达 1.5k 微指令)是它可能变慢的唯一原因的假设是非常错误的。

顺便说一句,Skylake 的微代码更新完全禁用了 LSD 以修复部分寄存器合并错误,错误 SKL150 1,事实上,除了一个小循环跨越 32B 边界并需要 2 个缓存线时,这实际上没有什么影响。

但 Agner 将JMP rel8/32JCC 吞吐量列为 HSW/SKL 上的 1-2 个周期,而 IvB 上仅为 2 个周期。因此,自 IvB 以来,除了 LSD 本身之外,关于采取分支的某些事情可能已经加速。

除了 LSD 之外,CPU 的某些部分可能还有一个特殊情况,用于长时间运行的小循环,让它们在 Haswell 及更高版本上每时钟运行 1 次跳转。我还没有测试什么条件会导致 HSW/SKL 上的 1 与 2 周期采用分支吞吐量。另请注意,Agner 在勘误 SKL150 的微码更新之前测量。


脚注 1:请参阅Haswell/Skylake 上的部分寄存器的性能如何?编写 AL 似乎对 RAX 有错误的依赖,而 AH 是不一致的,请注意 SKX 和 Kaby Lake 附带的微代码已经包含此内容。它最终在 CannonLake / Ice Lake 等 CPU 中重新启用,修复了错误的硬连线逻辑,因此可以安全地再次启用 LSD。

(我之前认为 Coffee Lake 已经重新启用了 LSD,但似乎没有 - wikichip明确表示它仍然被禁用,所以我认为这纠正了一些早期的报告,即它已重新启用。不过,CFL 确实修复了 L1TF 和 Meltdown 漏洞,专门针对这些漏洞使软件缓解变得不必要。)

于 2019-01-19T03:16:55.057 回答