当执行 uop 计数不是处理器宽度倍数的循环时,性能是否会降低?

2024-03-31

我想知道不同大小的循环在最新的 x86 处理器上如何执行,作为 uop 数量的函数。

这里引用 Peter Cordes 的一段话,他在《中》中提出了非 4 倍数的问题另一个问题 https://stackoverflow.com/a/31027695/149138:

我还发现循环缓冲区外的 uop 带宽不是 如果循环不是 4 uop 的倍数,则每个周期常数 4。 (IE。 是 abc、abc、...;不是 abca、bcab,...)。 Agner Fog 的微架构文档 不幸的是,不清楚循环缓冲区的限制。

问题在于循环是否需要是 N uop 的倍数才能以最大 uop 吞吐量执行,其中 N 是处理器的宽度。 (即,最新的英特尔处理器为 4)。在谈论“宽度”和微指令计数时,有很多复杂的因素,但我最想忽略这些。特别是,假设没有微观或宏观融合。

Peter 给出了以下循环体中包含 7 个微指令的循环示例:

7-uop 循环将发出 4|3|4|3|...我还没有测试过更大的组 循环(不适合循环缓冲区)以查看是否可以 下一次迭代中发出的第一条指令 group 作为它的分支,但我认为不是。

更一般地说,该声明是循环的每次迭代x它体内的微指令至少需要ceil(x / 4)迭代,而不是简单地x / 4.

对于某些或所有最新的 x86 兼容处理器来说,情况是否如此?


我对 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 nops, 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/迭代开始)由旧解码器处理,并显示出非常可预测的模式:

  • 所有循环都带有Nuops 正好取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 微指令的缓冲区中展开。

上面讨论的三个区域的颜色不同,至少可以看到相互竞争的效果:

  1. 在其他条件相同的情况下,涉及的微指令数量越多,效率就越低。命中是每次迭代仅一次的固定成本,因此较大的循环付出的代价较小relative cost.

  2. 当您进入 33+ uop 区域时,效率会大幅上升:吞吐量损失的大小都会增加,受影响的 uop 计数也会加倍。

  3. 第一个区域有些混乱,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 .odsGitHub 中的格式也是如此。


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.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

当执行 uop 计数不是处理器宽度倍数的循环时,性能是否会降低? 的相关文章

  • 在 x86 Intel VT-X 非根模式下,是否可以在每个指令边界传递中断?

    除了不将中断传送到虚拟处理器的某些正常指定条件 cli if 0 等 之外 客户机中的所有指令实际上都是可中断的吗 也就是说 当传入的硬件中断先传递给 LAPIC 然后传递给处理器时 据说会发生一些内部魔法 将其转换为虚拟中断给来宾 使用虚
  • 使用 html 属性的 DOM 惩罚

    我正在考虑使用 HTML5 数据属性来更轻松地编写我的应用程序的第三方脚本 因此 考虑两种情况 页面上有 10 000 个 HTML 元素 例如 div Sticker div 还有其他 10 000 个 HTML 元素 例如 div St
  • 如何使用 VBA 将符号/图标格式化为单元格而不使用条件格式

    我使用 VBA 代码放置条件格式以覆盖大型表格中的值 每个单元格使用 2 个公式来确定使用 3 个符号中的哪一个 我需要根据列使用不同的单元格检查每个单元格的值 因此据我了解 我必须将条件格式规则单独放置在每个单元格上 以确保每个单元格中的
  • 为什么 Visual Studio 使用 xchg ax,ax

    我正在查看程序的反汇编 因为它崩溃了 并注意到很多 xchg ax ax 我用谷歌搜索了一下 发现它本质上是一个 nop 但是为什么 Visual Studio 会执行 xchg 而不是 noop 该应用程序是一个C NET3 5 64位应
  • 数百个空闲线程的影响

    我正在考虑使用可能数百个线程来实现通过网络管理设备的任务 这是一个在带有 Linux 内核的 powerpc 处理器上运行的 C 应用程序 在每个任务进行同步以将数据从设备复制到任务的初始阶段之后 任务变得空闲 并且仅在收到警报或需要更改一
  • CSS:它渲染“ul > li”比“ul li”更快吗?

    正如我从少数人那里听说的那样 使用 gt 而不是 使渲染速度更快 slide hover gt div gt span border color c8c8c8 OR slide hover div span border color c8c
  • Verilog 双向握手示例

    我正在完成一个项目 要求是处理器内部功能单元之间的双向握手 我知道它是什么 但是有没有任何 标准 或一个简单的例子 我唯一能想到的就是两个单元之间 当它们之间有一条数据线并且当 X 发送到 Y 时 会给出一个单独的 发送 信号 当 Y 接收
  • 为什么 GCC 不将 a*a*a*a*a*a 优化为 (a*a*a)*(a*a*a)?

    我正在对科学应用程序进行一些数值优化 我注意到的一件事是 GCC 会优化调用pow a 2 通过将其编译成a a 但是调用pow a 6 没有优化 实际会调用库函数pow 这大大降低了性能 相比之下 英特尔 C 编译器 http en wi
  • MSMQ 慢速队列读取

    我正在使用一个开源 Net 库 它在底层使用 MSMQ 大约一两周后 服务速度变慢 时间不准确 但一般猜测 看来发生的情况是来自 MSMQ 的消息每 10 秒才被读取一次 通常 它们会立即被读取 因此 它们将在 T 10 秒 T 20 秒
  • 使用循环计算 Python 字典中元素的有效方法

    我有一个值列表 我希望在循环期间计算每个类的元素数量 即 1 2 3 4 5 mylist 1 1 1 1 1 1 2 3 2 2 2 2 3 3 4 5 5 5 5 mydict dict for index in mylist mydi
  • 在 R 中替换数据帧中最低列表值的最有效方法

    我有一个数据框 df 其中包含为每个受试者记录的数字列表 向量 用于测试项目的两次重复 subj item rep vec s1 1 1 2 1 4 5 8 4 7 s1 1 2 1 1 3 4 7 5 3 s1 2 1 6 5 4 1 2
  • 在 Matlab 中快速加载大块二进制文件

    我有一些相当大的 int16 格式的数据文件 256 个通道 大约 75 1 亿个样本 每个文件约 40 50 GB 左右 它以平面二进制格式编写 因此结构类似于 CH1S1 CH2S1 CH3S1 CH256S1 CH1S2 CH2S2
  • 我想优化这个短循环

    我想优化这个简单的循环 unsigned int i while j 0 j is an unsigned int with a start value of about N 36 000 000 float sub 0 i 1 unsig
  • Python:多重分配与单独分配速度

    我一直在寻求从我的代码中挤出更多的性能 最近 在浏览时这个 Python 维基页面 https wiki python org moin PythonSpeed 我发现了这个说法 多重分配比单独分配慢 例如 x y a b 比 x a y
  • 对大数据块进行反应非阻塞渲染

    最近我开始学习反应并想知道是否有某种模式可以用于大数据的非阻塞 UI 线程渲染 比方说 我们采取这个例子 https www mendix com tech blog making react reactive pursuit high p
  • 68HC11计算sin(x)的汇编代码

    68HC11 使用泰勒级数或查找表计算正弦值的汇编代码是什么 显示值只能是整数 查找表如何工作 在这种情况下 如何使用它来实现泰勒级数 http en wikipedia org wiki Taylor series 如果您正在寻找浮点解决
  • 在 JavaScript 中嵌套“switch”案例:有速度优势吗?

    这里有新手问题 我有一个包含大量字符串的 开关 像这样按字母顺序拆分是否有速度优势 switch myString substring 0 1 case a switch myString case a string beginning w
  • glBlitFramebuffer 渲染缓冲区和渲染全屏纹理哪个更快?

    哪个更快更高效 使用 OpenGL 纹理作为 CUDA 表面并在四边形上渲染 新样式 使用渲染缓冲区作为 CUDA 表面并使用 glBlitFramebuffer 进行渲染 None
  • PrintStream是有缓冲的,但是flush不会降低性能,而BufferedOutputStream会加速性能

    我预计由于 PrintStream 是缓冲的 通过在每次 print 之后添加刷新操作 速度性能应该会显着降低 但事实并非如此 如下面的代码片段所示 此外 将 PrintStream 包裹在 BufferedOutputStream 周围可
  • 有没有办法使用 i387 fsqrt 指令获得正确的舍入?

    有没有办法使用 i387 fsqrt 指令获得正确的舍入 除了改变精确模式在 x87 控制字中 我知道这是可能的 但这不是一个合理的解决方案 因为它存在令人讨厌的重入型问题 如果 sqrt 操作中断 精度模式将出错 我正在处理的问题如下 x

随机推荐