LSD可以从检测到的循环的下一次迭代中发出uOP吗?

Mar*_*oom 9 x86 assembly cpu-architecture intel-pmu

我正在使用一个非常简单的循环开始调查我的Haswell端口0上的分支单元的功能:

BITS 64
GLOBAL _start

SECTION .text

_start:

 mov ecx, 10000000

.loop:

 dec ecx             ;|
  jz .end            ;| 1 uOP (call it D)

jmp .loop            ;| 1 uOP (call it J)

.end:
 mov eax, 60
 xor edi, edi
 syscall
Run Code Online (Sandbox Code Playgroud)

使用perf我们看到循环以1c/iter运行

Performance counter stats for './main' (50 runs):

        10,001,055      uops_executed_port_port_6   ( +-  0.00% )
         9,999,973      uops_executed_port_port_0   ( +-  0.00% )
        10,015,414      cycles:u                    ( +-  0.02% )
                23      resource_stalls_rs          ( +- 64.05% )
Run Code Online (Sandbox Code Playgroud)

我对这些结果的解释是:

  • D和J都是并行发送的.
  • J具有1个周期的倒数吞吐量.
  • D和J都以最佳方式发送.

但是,我们也可以看到RS永远不会满员.
它最多可以以2 uOPs/c的速率发送uOP,但理论上可以得到4 uOPs/c,导致大约30 c的完整RS(对于具有60个融合域条目的RS).

根据我的理解,应该有很少的分支误预测,而且uOP都应该来自LSD.
所以我看了FE:

     8,239,091      lsd_cycles_active ( +-  3.10% )
       989,320      idq_dsb_cycles    ( +- 23.47% )
     2,534,972      idq_mite_cycles   ( +- 15.43% )
         4,929      idq_ms_uops       ( +-  8.30% )

   0.007429733 seconds time elapsed   ( +-  1.79% )
Run Code Online (Sandbox Code Playgroud)

确认FE从LSD 1发出.
但是,LSD永远不会发出4个uOPs/c:

     7,591,866      lsd_cycles_active ( +-  3.17% )
             0      lsd_cycles_4_uops 
Run Code Online (Sandbox Code Playgroud)

我的解释是LSD不能从下一次迭代2发出uOP,因此每个周期只发送DJ对到BE.
我的解释是否正确?


源代码在此存储库中.


1存在一些差异,我认为这是由于允许某些上下文切换的大量迭代.
2在具有有限电路深度的硬件中,这听起来相当复杂.

Had*_*ais 5

循环中的所有微指令都是分支(每次迭代2个)。我认为`lsd_cycles_4_uops为零的原因是由于重命名器中的限制。根据《英特尔优化手册》第2.4.3.1节:

与之前的微体系结构中的每个分支一个分支相比,重命名器每个周期可以分配两个分支。这样可以消除执行过程中的气泡。

这是有关桑迪桥微体系结构的一部分的小节。但是据我所知,这适用于所有以后的微体系结构。每个周期最大重命名吞吐量为4微秒。但是最多两个对象可以是分支。因此,在所有微指令都是分支的示例中,即使在循环的第一次迭代中,LSD在任何给定的周期也永远不会传递超过2微指令。

因此,将在每个周期中在RS中分配2个分支uo,并且每个周期都可以调度这两个分支。因此,RS占用率不会增加。

此限制不会影响程序的性能。每个周期执行2个分支指令,每个周期提供3个IPC已经是最佳选择。

我试图找到一个性能事件,由于该限制,该事件可以捕获分配器停顿。在这种情况下,事件RESOURCE_STALLS.ANY和UOPS_ISSUED.ANY(以及cmask= 1和inv= 1)似乎无关紧要。@IwillnotexistIdonotexist建议使用 IDQ_UOPS_NOT_DELIVERED.CORE。我在下面介绍了性能事件及其所有受支持的变体的结果。我也提供了这些事件的正确含义,因为该手册是错误的。T表示迭代次数。

IDQ_UOPS_NOT_DELIVERED.CORE:计算分配器未使用的插槽数。如果程序运行了C个核心周期,则插槽总数为4 * C。测量值几乎等于2 * T。由于周期数为T,因此时隙数为4 * T,这意味着大约没有使用发行时隙的一半。

IDQ_UOPS_NOT_DELIVERED.CYCLES_0_UOPS_DELIV.CORE:计算从IDQ传递零微分的周期数。测量值可以忽略不计。

IDQ_UOPS_NOT_DELIVERED.CYCLES_LE_1_UOP_DELIV.CORE:计算从IDQ传送最多1微秒的周期数。测量值可以忽略不计。

IDQ_UOPS_NOT_DELIVERED.CYCLES_LE_2_UOP_DELIV.CORE:计算从IDQ传送最多2微秒的周期数:测量值几乎等于T。

IDQ_UOPS_NOT_DELIVERED.CYCLES_LE_3_UOP_DELIV.CORE:计算从IDQ最多发送3微秒的周期数:测量值几乎等于T。

因此,由于执行时间几乎等于T个核心周期,因此可以得出结论,分配器在大多数周期中每个周期只分配正好2 oups,这等于分配率。

请注意,Haswell和Skylake中的RS包含未融合的uops。因此,每个条目都可以容纳一个未融合的uop。参见脚注2。但这无关紧要,因为没有微融合。

  • 实际上,这正是我所期望的:2T未完成的广告。回想一下,在Haswell上,解码器执行ouop的宏融合,因此dec + jz和jmp构成了两个uops,用于计算从IDQ到RAT的传输。一旦RS填满48个分支uops,IDQ确实将无法在每个时钟周期向RAT提供可能的4 uops中的2个,因为RAT并没有停滞[((它有足够的空间容纳其他东西) ](https://www.realworldtech.com/haswell-cpu/3/),RAT不能在其分支缓冲区中接受48个以上的分支,并且以2 uops / cc的速度消耗。 (2认同)