我对 Linux 做了一些调查perf
帮助在我的 Skylake 上回答这个问题i7-6700HQ http://ark.intel.com/products/88967/Intel-Core-i7-6700HQ-Processor-6M-Cache-up-to-3_50-GHz框,Haswell 结果由另一位用户提供。下面的分析适用于 Skylake,但随后与 Haswell 进行了比较。
Other architectures may vary0, and to help sort it all out I welcome additional results. The source is available https://github.com/travisdowns/x86-loop-test).
这个问题主要涉及前端,因为在最近的架构中,前端对每个周期施加了四个融合域微指令的硬限制。
循环性能规则摘要
首先,我将根据一些在处理小循环时要记住的“性能规则”来总结结果。还有许多其他性能规则 - 这些规则是对它们的补充(即,您可能不会为了满足这些规则而违反其他规则)。这些规则最直接适用于 Haswell 及更高版本的架构 - 请参阅其他答案 https://stackoverflow.com/a/53148682/149138概述早期架构的差异。
首先,统计一下数量宏融合你的循环中的微指令。你可以使用阿格纳的指令表 http://www.agner.org/optimize/#manual_instr_tab直接查找每条指令,除了 ALU uop 和紧随其后的分支通常会融合在一起形成一个 uop。然后根据这个计数:
- 如果计数是 4 的倍数,那就很好:这些循环执行效果最佳。
- 如果计数为偶数且小于 32,则很好,除非计数为 10,在这种情况下,如果可以的话,您应该展开到另一个偶数。
- 对于奇数,如果可以的话,您应该尝试展开到小于 32 或 4 的倍数的偶数。
- 对于大于 32 微指令但小于 64 的循环,如果它还不是 4 的倍数,您可能需要展开:如果超过 64 微指令,您将在 Sklyake 上的任何值和 Haswell 上的几乎所有值上获得高效的性能(有一些偏差,可能与对齐有关)。这些循环的低效率仍然相对较小:最需要避免的值是
4N + 1
计数,然后是4N + 2
counts.
调查结果摘要
对于从 uop 缓存提供的代码,不存在明显的 4 倍数效应。可以以每周期 4 个融合域微指令的吞吐量执行任意数量微指令的循环。
对于传统解码器处理的代码,情况正好相反:循环执行时间仅限于整数个周期,因此不是 4 微指令的倍数的循环无法实现 4 微指令/周期,因为它们浪费了一些发出/执行槽。
对于从环路流检测器 (LSD) 发出的代码,情况是两者的混合,下面将更详细地解释。一般来说,小于 32 微指令且具有偶数微指令的循环能够以最佳方式执行,而奇数大小的循环则不然,并且较大的循环需要 4 倍微指令计数才能以最佳方式执行。
英特尔怎么说
英特尔实际上在其优化手册中对此做了说明,其他答案中有详细信息。
Details
熟悉最新 x86-64 架构的任何人都知道,在任何时候,前端的获取和解码部分都可能以几种不同的模式工作,具体取决于代码大小和其他因素。事实证明,这些不同的模式在循环大小调整方面都有不同的行为。接下来我将分别介绍它们。
传统解码器
The legacy decoder1 is the full machine-code-to-uops decoder that is used2 when the code doesn't fit in the uop caching mechanisms (LSD or DSB). The primary reason this would occur is if the code working set is larger than the uop cache (approximately ~1500 uops in the ideal case, less in practice). For this test though, we'll take advantage of the fact that the legacy decoder will also be used if an aligned 32-byte chunk contains more than 18 instructions3.
为了测试旧解码器的行为,我们使用如下所示的循环:
short_nop:
mov rax, 100_000_000
ALIGN 32
.top:
dec rax
nop
...
jnz .top
ret
Basically, a trivial loop that counts down until rax
is zero. All instructions are a single uop4 and the number of nop
instructions is varied (at the location shown as ...
) to test different sizes of loops (so a 4-uop loop will have 2 nop
s, plus the two loop control instructions). There is no macro-fusion as we always separate the dec
and jnz
with at least one nop
, and also no micro-fusion. Finally, there is no memory access at (outside of the implied icache access).
Note that this loop is very dense - about 1 byte per instruction (since the nop
instructions are 1 byte each) - so we'll trigger the > 18 instructions in a 32B chunk condition as soon as hit 19 instructions in the loop. Based on examining the perf
performance counters lsd.uops
and idq.mite_uops
that's exactly what we see: essentially 100% of the instructions come out of the LSD5 up until and including the 18 uop loop, but at 19 uops and up, 100% come from the legacy decoder.
In any case, here are the cycles/iteration for all loop sizes from 3 to 99 uops6:
蓝点是适合 LSD 的循环,显示出一些复杂的行为。我们稍后会看看这些。
红点(从 19 uops/迭代开始)由旧解码器处理,并显示出非常可预测的模式:
- 所有循环都带有
N
uops 正好取ceiling(N/4)
迭代
因此,至少对于传统解码器来说,Peter 的观察完全适用于 Skylake:循环4 uop 的倍数可以以 4 的 IPC 执行,但任何其他数量的微指令将浪费 1、2 或 3 个执行槽(对于带有4N+3
, 4N+2
, 4N+1
分别说明)。
我不清楚为什么会发生这种情况。尽管如果考虑到解码发生在连续的 16B 块中,这似乎是显而易见的,因此在 4 uops/周期循环的解码速率下,不是 4 的倍数的循环中总会有一些尾随(浪费)时隙,jnz
遇到指令。然而,实际的获取和解码单元由预解码和解码阶段组成,中间有一个队列。预解码阶段的吞吐量实际上为6指令,但在每个周期仅解码到 16 字节边界的末尾。这似乎意味着循环末尾出现的气泡可以被预解码器 -> 解码队列吸收,因为预解码器的平均吞吐量高于 4。
所以我无法根据我对预解码器工作原理的理解来完全解释这一点。解码或预解码中可能存在一些阻止非整数循环计数的附加限制。例如,即使跳转后的指令在预解码队列中可用,传统解码器也可能无法解码跳转两侧的指令。也许这与需要有关handle http://www.agner.org/optimize/blog/read.php?i=142#272宏观融合。
上面的测试显示了循环顶部在 32 字节边界上对齐的行为。下面是同一张图,但添加了一个系列,显示了循环顶部向上移动 2 个字节时的效果(即,现在在 32N + 30 边界处未对齐):
现在,大多数循环大小都会受到 1 或 2 个周期的影响。当您考虑解码 16B 边界和每周期解码 4 条指令时,1 个惩罚情况是有意义的,而 2 个周期惩罚情况发生在循环中,由于某种原因 DSB 用于循环中的 1 条指令(可能是dec
指令出现在它自己的 32 字节块中),并且会产生一些 DSBMITE 切换惩罚。
在某些情况下,当最终更好地对齐环路末端时,未对准并不会造成伤害。我测试了未对准情况,它以同样的方式持续到 200 uop 循环。如果您从表面上理解预解码器的描述,似乎如上所述,它们应该能够隐藏未对齐的获取气泡,但它不会发生(可能队列不够大)。
DSB(Uop 缓存)
The uop cache (Intel likes to call it the DSB) is able to cache most loops of moderate amount of instructions. In a typical program, you'd hope that most of your instructions are served out of this cache7.
我们可以重复上面的测试,但现在从 uop 缓存中提供 uop。将 nops 的大小增加到 2 个字节很简单,因此我们不再达到 18 条指令的限制。我们使用 2 字节 nopxchg ax, ax
在我们的循环中:
long_nop_test:
mov rax, iters
ALIGN 32
.top:
dec eax
xchg ax, ax ; this is a 2-byte nop
...
xchg ax, ax
jnz .top
ret
在这里,结果非常简单。对于 DSB 交付的所有测试环路尺寸,所需的周期数为N/4
- 即,以最大理论吞吐量执行的循环,即使它们没有 4 uop 的倍数。因此,一般来说,在 Skylake 上,由 DSB 提供的中等大小的循环不需要担心确保 uop 计数满足某个特定倍数。
这是一张包含 1,000 个 uop 循环的图表。如果您眯着眼睛,您可以看到 64-uop 之前的次优行为(当循环位于 LSD 中时)。之后,这是一个直接的镜头,4 IPC 一直到 1,000 uops(有一个大约 900 的信号,可能是由于我的盒子上的负载造成的):
接下来我们看看足够小以适合 uop 缓存的循环的性能。
LSD(环路蒸汽检测器)
重要的提示:英特尔显然已经disabledSkylake(SKL150 勘误表)和 Kaby Lake(KBL095、KBW095 勘误表)芯片上的 LSD 通过微代码更新以及开箱即用的 Skylake-X 上的 LSD 实现,因为a bug http://gallium.inria.fr/blog/intel-skylake-bug/与超线程和 LSD 之间的交互有关。对于这些芯片,下图可能不会包含高达 64 uops 的有趣区域;相反,它看起来与 64 uop 之后的区域相同。
循环流检测器可以缓存高达 64 uops 的小循环(在 Skylake 上)。在英特尔最近的文档中,它更多地被定位为一种节能机制,而不是一种性能功能——尽管肯定没有提到使用 LSD 的性能缺点。
针对应适合 LSD 的循环大小运行此命令,我们得到以下循环/迭代行为:
这里的红线是从 LSD 传递的微指令的百分比。对于 5 到 56 uop 的所有环路大小,它都保持 100% 平坦。
对于 3 和 4 uop 循环,我们有不寻常的行为,即 16% 和 25% 的 uop 分别是从旧解码器传递的。啊?幸运的是,它似乎不会影响循环吞吐量,因为两种情况都达到了 1 个循环/周期的最大吞吐量 - 尽管人们可能会遇到一些 MITELSD 转换惩罚。
在 57 到 62 微指令的循环大小之间,从 LSD 传送的微指令数量表现出一些奇怪的行为 - 大约 70% 的微指令从 LSD 传送,其余的从 DSB 传送。 Skylake 名义上具有 64 uop LSD,因此这是超出 LSD 大小之前的某种转换 - 也许 IDQ(在其上实现 LSD)内存在某种内部对齐,导致仅部分命中LSD在这个阶段。这个阶段很短,而且就性能而言,似乎主要是之前的全 LSD 性能和之后的全 DSB 性能的线性组合。
让我们看一下 5 到 56 uop 之间结果的主体。我们看到三个不同的区域:
Loops from 3 to 10 uops: Here, the behavior is complex. It is the only region where we see cycle counts that can't be explained by static behavior over a single loop iteration8. The range is short enough that it's hard to say if there is a pattern. Loops of 4, 6 and 8 uops all execute optimally, in N/4
cycles (that's the same pattern as the next region).
另一方面,10 微指令的循环每次迭代执行 2.66 个周期,这使得它成为唯一一个无法以最佳方式执行的偶数循环大小,直到达到 34 微指令或以上的循环大小(26 处的异常值除外) 。这对应于类似重复 uop/周期执行率4, 4, 4, 3
。对于 5 uop 的循环,每次迭代获得 1.33 个周期,非常接近,但与理想的 1.25 不同。这对应于执行率4, 4, 4, 4, 3
.
这些结果很难解释。结果在每次运行中都是可重复的,并且对更改具有鲁棒性,例如将 nop 替换为实际执行类似操作的指令mov ecx, 123
。这可能与每 2 个周期 1 个分支的限制有关,该限制适用于除“非常小”的循环之外的所有循环。微指令可能偶尔会排列起来,从而导致此限制生效,从而导致额外的周期。一旦达到 12 uop 或以上,这种情况就不会发生,因为每次迭代总是至少需要三个周期。
从 11 到 32-uop 循环:我们看到一个阶梯图案,但周期为两个。基本上所有循环都带有even微指令的数量表现最佳 - 即,准确地采取N/4
循环。具有奇数个微指令的循环会浪费一个“发出槽”,并且与具有多个微指令的循环占用相同数量的周期(即,17 个微指令的循环与 18 个微指令的循环占用相同的 4.5 个周期)。所以这里我们的行为比ceiling(N/4)
对于许多 uop 计数,我们有第一个证据表明 Skylake 至少可以以非整数个周期执行循环。
唯一的异常值是 N=25 和 N=26,两者都比预期时间长约 1.5%。它很小但可重复,并且可以在文件中移动函数。这太小了,无法用每次迭代效应来解释,除非它有一个巨大的周期,所以它可能是别的东西。
这里的整体行为与硬件完全一致(除了 25/26 异常)展开循环2 倍。
从 33 到 ~64 uops 的循环:我们再次看到阶梯模式,但周期为 4,并且平均性能比高达 32 uop 的情况更差。行为正是ceiling(N/4)
- 也就是说,与传统解码器的情况相同。因此,对于 32 至 64 uops 的循环,LSD 相对于传统解码器没有提供明显的优势,就这个特定限制的前端吞吐量而言。当然,LSD 还有很多其他更好的地方——它避免了更复杂或更长的指令可能出现的许多潜在解码瓶颈,并且节省电量等等。
所有这些都非常令人惊讶,因为这意味着从 uop 缓存传递的循环通常会执行better尽管 LSD 通常被定位为比 DSB 严格更好的微指令源(例如,作为尝试保持循环足够小以适合 LSD 的建议的一部分)。
这是查看相同数据的另一种方法 - 相对于给定 uop 计数的效率损失,与每周期 4 uop 的理论最大吞吐量。 10% 的效率意味着您只有通过简单计算得出的吞吐量的 90%N/4
公式。
这里的整体行为与不执行任何展开的硬件一致,这是有道理的,因为超过 32 微指令的循环根本无法在 64 微指令的缓冲区中展开。
上面讨论的三个区域的颜色不同,至少可以看到相互竞争的效果:
在其他条件相同的情况下,涉及的微指令数量越多,效率就越低。命中是每次迭代仅一次的固定成本,因此较大的循环付出的代价较小relative cost.
当您进入 33+ uop 区域时,效率会大幅上升:吞吐量损失的大小都会增加,受影响的 uop 计数也会加倍。
第一个区域有些混乱,7 个微指令是最差的总体微指令计数。
结盟
上面的 DSB 和 LSD 分析是针对与 32 字节边界对齐的循环条目,但未对齐的情况似乎在这两种情况下都没有受到影响:与对齐的情况没有重大差异(除了可能有一些小的变化)不到 10 uop,我没有进一步调查)。
这是未对齐的结果32N-2
and 32N+2
(即循环32B边界前后2个字节):
理想N/4
还显示了线以供参考。
Haswell
接下来接下来看看之前的微架构:Haswell。这里的数字是由用户慷慨提供的我将不存在 我不存在 https://stackoverflow.com/users/2809095/iwillnotexist-idonotexist.
LSD + 传统解码管道
首先,“密集代码”测试的结果,该测试测试 LSD(对于小 uop 计数)和遗留管道(对于较大的 uop 计数,因为循环由于指令密度而“破坏”了 DSB。
我们立即就看到了差异when每个架构都从 LSD 提供微指令以实现密集循环。下面我们比较 Skylake 和 Haswell 的短循环dense代码(每条指令 1 个字节)。
如上所述,Skylake 循环在恰好 19 微指令时停止从 LSD 传送,正如每 32 字节区域 18 微指令的代码限制所预期的那样。另一方面,Haswell 似乎也停止从 LSD 可靠地交付 16-uop 和 17-uop 循环。对此我没有任何解释。 3 uop 的情况也有一个区别:奇怪的是,两个处理器都只提供some在 3 和 4 uop 的情况下,它们的 uop 超出了 LSD,但 4 uop 的确切数量是相同的,与 3 不同。
但我们最关心的是实际表现,对吧?那么让我们看看 32 字节对齐的周期/迭代dense代码案例:
这与上面显示的 Skylake 数据相同(未对齐的系列已被删除),Haswell 绘制在旁边。您会立即注意到该模式是similar对于 Haswell,但不一样。如上,这里有两个区域:
旧版解码
大于~16-18 uop(不确定性如上所述)的循环是从传统解码器传递的。 Haswell 的模式与 Skylake 有所不同。
对于 19-30 uop 的范围,它们是相同的,但此后 Haswell 打破了这一模式。 Skylake 拍摄ceil(N/4)
从传统解码器传送的循环周期。另一方面,Haswell 似乎采取了类似的做法ceil((N+1)/4) + ceil((N+2)/12) - ceil((N+1)/12)
。好吧,这很混乱(更简短的形式,有人吗?) - 但基本上这意味着,虽然 Skylake 以 4*N 周期最佳执行循环(即,以 4-uops/周期),但此类循环(本地)通常是least最佳计数(至少在本地)- 执行此类循环比 Skylake 多花一个周期。所以你实际上最好在 Haswell 上使用 4N-1 uops 循环,except25% 的此类循环是also16-1N 形式(31、47、63 等)需要一个额外的周期。这听起来像是闰年的计算——但通过上面的视觉效果可能可以最好地理解这种模式。
我不认为这个模式是固有的到 Haswell 上的 uop 调度,所以我们不应该对其进行过多的阅读。似乎可以解释为
0000000000455a80 <short_nop_aligned35.top>:
16B cycle
1 1 455a80: ff c8 dec eax
1 1 455a82: 90 nop
1 1 455a83: 90 nop
1 1 455a84: 90 nop
1 2 455a85: 90 nop
1 2 455a86: 90 nop
1 2 455a87: 90 nop
1 2 455a88: 90 nop
1 3 455a89: 90 nop
1 3 455a8a: 90 nop
1 3 455a8b: 90 nop
1 3 455a8c: 90 nop
1 4 455a8d: 90 nop
1 4 455a8e: 90 nop
1 4 455a8f: 90 nop
2 5 455a90: 90 nop
2 5 455a91: 90 nop
2 5 455a92: 90 nop
2 5 455a93: 90 nop
2 6 455a94: 90 nop
2 6 455a95: 90 nop
2 6 455a96: 90 nop
2 6 455a97: 90 nop
2 7 455a98: 90 nop
2 7 455a99: 90 nop
2 7 455a9a: 90 nop
2 7 455a9b: 90 nop
2 8 455a9c: 90 nop
2 8 455a9d: 90 nop
2 8 455a9e: 90 nop
2 8 455a9f: 90 nop
3 9 455aa0: 90 nop
3 9 455aa1: 90 nop
3 9 455aa2: 90 nop
3 9 455aa3: 75 db jne 455a80 <short_nop_aligned35.top>
在这里,我记下了每条指令出现的 16B 解码块 (1-3),以及它将被解码的周期。规则基本上是,只要接下来的 4 条指令落入当前 16B 块中,就会被解码。否则他们必须等到下一个周期。对于 N=35,我们看到在第 4 个周期中丢失了 1 个解码槽(16B 块中仅剩下 3 个指令),但除此之外,循环与 16B 边界甚至最后一个周期都很好地对齐( 9) 可译码4条指令。
这是 N=36 的截断图,除了循环末尾之外,其他都是相同的:
0000000000455b20 <short_nop_aligned36.top>:
16B cycle
1 1 455a80: ff c8 dec eax
1 1 455b20: ff c8 dec eax
1 1 455b22: 90 nop
... [29 lines omitted] ...
2 8 455b3f: 90 nop
3 9 455b40: 90 nop
3 9 455b41: 90 nop
3 9 455b42: 90 nop
3 9 455b43: 90 nop
3 10 455b44: 75 da jne 455b20 <short_nop_aligned36.top>
现在,第三个也是最后一个 16B 块中有 5 条指令需要解码,因此需要一个额外的周期。基本上有35条指令,对于这种特定的指令模式恰好与 16B 位边界更好地对齐,并且在解码时节省了一个周期。这并不意味着 N=35 通常比 N=36 更好!不同的指令将具有不同的字节数并且排列方式也不同。类似的对齐问题也解释了每 16 个字节所需的额外周期:
16B cycle
...
2 7 45581b: 90 nop
2 8 45581c: 90 nop
2 8 45581d: 90 nop
2 8 45581e: 90 nop
3 8 45581f: 75 df jne 455800 <short_nop_aligned31.top>
这里是决赛jne
已滑入下一个 16B 块(如果指令跨越 16B 边界,则它实际上位于后一个块中),导致额外的周期丢失。这种情况仅每 16 个字节发生一次。
So the Haswell legacy decoder results are explained perfectly by a legacy decoder that behaves as described, for example, in Agner Fog's microarchitecture doc http://www.agner.org/optimize/#manual_microarch. In fact, it also seems to explain Skylake results if you assume Skylake can decode 5 instructions per cycle (delivering up to 5 uops)9. Assuming it can, the asymptotic legacy decode throughput on this code for Skylake is still 4-uops, since a block of 16 nops decodes 5-5-5-1, versus 4-4-4-4 on Haswell, so you only get benefits at the edges: in the N=36 case above, for example, Skylake can decode all remaining 5 instructions, versus 4-1 for Haswell, saving a cycle.
结果是,遗留解码器的行为似乎可以以相当简单的方式理解,主要的优化建议是继续修改代码,使其“智能”地落入 16B 对齐的块中(也许这就是 NP-像垃圾箱一样硬?)。
DSB(又是LSD)
接下来让我们看一下从 LSD 或 DSB 提供代码的场景 - 通过使用“long nop”测试,该测试可以避免打破每 32B 块 18-uop 的限制,因此保留在 DSB 中。
Haswell 与 Skylake:
请注意 LSD 行为 - 此处 Haswell 在 57 uops 时停止从 LSD 中提供服务,这与已发布的 57 uops 的 LSD 大小完全一致。没有像我们在 Skylake 上看到的那样奇怪的“过渡期”。 Haswell 对于 3 和 4 微指令也有奇怪的行为,其中分别只有约 0% 和约 40% 的微指令来自 LSD。
在性能方面,Haswell 通常与 Skylake 一致,但有一些偏差,例如,大约 65、77 和 97 uops,四舍五入到下一个周期,而 Skylake 始终能够维持 4 uops/周期,即使结果如此在非整数个周期中。与预期 25 和 26 uop 的轻微偏差已经消失。也许 Skylake 的 6 uop 传输速率有助于避免 Haswell 在 4 uop 传输速率时遇到的 uop 缓存对齐问题。
其他架构
以下附加架构的结果由用户 Andreas Abel 善意提供,但我们必须使用另一个答案进行进一步分析,因为我们处于此处的字符限制。
需要帮助
尽管社区善意地提供了许多平台的结果,但我仍然对早于 Nehalem 和新于 Coffee Lake 的芯片(特别是 Cannon Lake,它是一个新的 uarch)上的结果感兴趣。生成这些结果的代码是公开的 https://github.com/travisdowns/x86-loop-test。另外,上面的结果可用 https://github.com/travisdowns/x86-loop-test/blob/master/loop_test.ods in .ods
GitHub 中的格式也是如此。
0 In particular, the legacy decoder maximum throughput apparently increased from 4 to 5 uops in Skylake, and the maximum throughput for the uop cache increased from 4 to 6. Both of those could impact the results described here.
1 Intel actually like to call the legacy decoder the MITE (Micro-instruction Translation Engine), perhaps because it's a faux-pas to actually tag any part of your architecture with the legacy connotation.
2 Technically there is another, even slower, source of uops - the MS (microcode sequencing engine), which is used to implement any instruction with more than 4 uops, but we ignore this here since none of our loops contain microcoded instructions.
3 This works because any aligned 32-byte chunk can use at most 3-ways in its uop cache slot, and each slot holds up to 6 uops. So if you use more than 3 * 6 = 18
uops in a 32B chunk, the code can't be stored in the uop cache at all. It's probably rare to encounter this condition in practice, since the code needs to be very dense (less than 2 bytes per instruction) to trigger this.
4 The nop
instructions decode to one uop, but don't are eliminated prior to execution (i.e., they don't use an execution port) - but still take up space in the front end and so count against the various limits that we are interested in.
5 The LSD is the loop stream detector, which caches small loops of up to 64 (Skylake) uops directly in the IDQ. On earlier architectures it can hold 28 uops (both logical cores active) or 56 uops (one logical core active).
6 We can't easily fit a 2 uop loop in this pattern, since that would mean zero nop
instructions, meaning the dec
and jnz
instructions would macro-fuse, with a corresponding change in the uop count. Just take my word that all loops with 4 or less uops execute at best at 1 cycle/iteration.
7 For fun, I just ran perf stat
against a short run of Firefox where I opened a tab and clicked around on a few Stack Overflow questions. For instructions delivered, I got 46% from DSB, 50% from legacy decoder and 4% for LSD. This shows that at least for big, branchy code like a browser the DSB still can't capture the large majority of the code (lucky the legacy decoders aren't too bad).
8 By this, I mean that all the other cycle counts can be explained by simply by taking an "effective" integral loop cost in uops (which might be higher than the actual size is uops) and dividing by 4. For these very short loops, this doesn't work - you can't get to 1.333 cycles per iteration by dividing any integer by 4. Said another way, in all other regions the costs have the form N/4 for some integer N.
9 In fact we know that Skylake can deliver 5 uops per cycle from the legacy decoder, but we don't know if those 5 uops can come from 5 different instructions, or only 4 or less. That is, we expect that Skylake can decode in the pattern 2-1-1-1
, but I'm not sure if it can decode in the pattern 1-1-1-1-1
. The above results give some evidence that it can indeed decode 1-1-1-1-1
.