首页 文章

Haswell / Skylake的部分寄存器究竟如何表现?写AL似乎对RAX有假依赖,而AH是不一致的

提问于
浏览
26

此循环在Intel Conroe / Merom上每3个周期运行一次,按预期方式在 imul 吞吐量上出现瓶颈 . 但是在Haswell / Skylake上,它每11个循环运行一次,显然是因为 setnz al 依赖于最后的 imul .

; synthetic micro-benchmark to test partial-register renaming
    mov     ecx, 1000000000
.loop:                 ; do{
    imul    eax, eax     ; a dep chain with high latency but also high throughput
    imul    eax, eax
    imul    eax, eax

    dec     ecx          ; set ZF, independent of old ZF.  (Use sub ecx,1 on Silvermont/KNL or P4)
    setnz   al           ; ****** Does this depend on RAX as well as ZF?
    movzx   eax, al
    jnz  .loop         ; }while(ecx);

如果 setnz al 依赖于 rax ,则3ximul / setcc / movzx序列形成循环携带的依赖链 . 如果不是,则每个 setcc / movzx / 3x imul 链都是独立的,与更新循环计数器的 dec 分开 . 在HSW / SKL上测量的每次迭代11c完全由延迟瓶颈解释:3x3c(imul)1c(由setcc读取 - 修改 - 写入)1c(movzx在同一寄存器中) .


Off topic: avoiding these (intentional) bottlenecks

我想采用可理解/可预测的行为来隔离部分注册的东西,而不是最佳性能 .

例如, xor -zero / set-flags / setcc 无论如何都更好(在这种情况下, xor eax,eax / dec ecx / setnz al ) . 这打破了所有CPU上的eax(除了像PII和PIII这样的早期P6系列),仍然避免了部分寄存器合并处罚,并节省了1c的 movzx 延迟 . 它还在CPU上使用了少量ALU uop handle xor-zeroing in the register-rename stage . 有关使用 setcc 进行xor-zeroing的更多信息,请参阅该链接 .

请注意,AMD,Intel Silvermont / KNL和P4根本不进行部分寄存器重命名 . 它只是英特尔P6系列CPU及其后代英特尔Sandybridge系列中的一项功能,但似乎已逐步淘汰 .

不幸的是,gcc确实倾向于使用 cmp / setcc al / movzx eax,al ,它可以使用 xor 而不是 movzx (Godbolt compiler-explorer example),而clang使用xor-zero / cmp / setcc,除非你结合多个布尔条件,如 count += (a==b) | (a==~b) .

xor / dec / setnz版本在Skylake,Haswell和Core2上每次迭代运行3.0c(瓶颈在 imul 吞吐量上) . xor -zeroing打破了对除PPro / PII / PIII /早期Pentium-M以外的所有无序CPU的旧值 eax 的依赖性(它仍然避免了部分注册合并处罚,但没有打破dep ) . Agner Fog's microarch guide describes this . 用 mov eax,0 替换xor-zeroing将其降低到Core2上每4.78个周期减1:当 imulsetnz al 之后读取 eax2-3c stall (in the front-end?) to insert a partial-reg merging uop .

另外,我使用 movzx eax, al ,它击败了mov-elimination,就像 mov rax,rax 一样 . (IvB,HSW和SKL可以使用0延迟重命名 movzx eax, bl ,但Core2不能) . 除了部分寄存器行为之外,这使得Core2 / SKL上的所有内容都相同 .


Core2行为与Agner Fog's microarch guide一致,但HSW / SKL行为不符合 . 从第11.10节到Skylake,以及之前的英特尔搜索:

通用寄存器的不同部分可以存储在不同的临时寄存器中,以消除错误依赖 .

遗憾的是,他没有时间对每个新的uarch进行详细测试以重新测试假设,因此这种行为的变化从裂缝中滑落 .

Agner确实描述了通过Skylake在Sandybridge上插入high8寄存器(AH / BH / CH / DH)以及SnB上的low8 / low16插入(不停止)合并uop . (遗憾的是,我过去一直散布错误的信息,并说Haswell可以免费合并AH . 我过快地浏览了Agner的Haswell部分,并且没有注意到后面关于high8寄存器的段落 . 如果你看到,请告诉我 . 我对其他帖子的错误评论,所以我可以删除它们或添加更正 . 我会尝试至少找到并编辑我的答案,我已经说过了 . )


我的实际问题: How exactly do partial registers really behave on Skylake?

Is everything the same from IvyBridge to Skylake, including the high8 extra latency?

Intel's optimization manual并不具体说明哪些CPU具有错误依赖性(虽然它确实提到某些CPU具有它们),并且省略了诸如读取AH / BH / CH / DH(high8寄存器)之类的东西,即使它们没有被修改了 .

如果有任何P6家族(Core2 / Nehalem)行为,Agner Fog的微观指南没有描述,那也会很有趣,但我应该将这个问题的范围限制在Skylake或Sandybridge家族 .


My Skylake test data ,将 %rep 4 短序列放入一个运行100M或1G迭代的小型 dec ebp/jnz 循环中 . 我使用Linux perf 以与in my answer here相同的方式在相同的硬件(桌面Skylake i7 6700k)上测量周期 .

除非另有说明,否则每条指令使用ALU作为1个融合域uop运行执行端口 . (用ocperf.py stat -e ...,uops_issued.any,uops_executed.thread测量) . 这检测到(没有)mov-elimination和额外的合并uops .

"4 per cycle"案例是对无限展开案例的推断 . 循环开销占用了一些前端带宽,但是每个周期优于1的任何东西都表明寄存器重命名避免了write-after-write output dependency,并且uop在内部不作为读 - 修改 - 写处理 .

Writing to AH only :阻止循环从环回缓冲区(又称循环流检测器(LSD))执行 . lsd.uops 的计数在HSW上正好为0,在SKL上为小(约为1.8k),并且不随循环迭代计数而缩放 . 可能这些计数来自某些内核代码 . 当循环从LSD运行时, lsd.uops ~= uops_issued 到测量噪声内 . 一些循环在LSD或no-LSD之间交替(例如,如果解码在错误的地方开始,它们可能不适合uop缓存),但是在测试时我没有碰到它 .

  • 重复 mov ah, bh 和/或 mov ah, bl 每循环运行4次 . 它需要一个ALU uop,所以它不像 mov eax, ebx 那样被淘汰 .

  • 重复 mov ah, [rsi] 每周期运行2次(负载吞吐量瓶颈) .

  • 重复 mov ah, 123 每循环运行1次 . (循环中的dep-breaking xor eax,eax消除了瓶颈 . )

  • 重复 setz ahsetc ah 每循环运行1次 . (破解 xor eax,eax 让它成为 setcc 和循环分支的p06吞吐量的瓶颈 . )

Why does writing ah with an instruction that would normally use an ALU execution unit have a false dependency on the old value, while mov r8, r/m8 doesn't (for reg or memory src)? (那么 mov r/m8, r8 怎么样?你用reg-reg运行的两个操作码中哪一个无关紧要?)

  • 重复 add ah, 123 按预期每周期运行1次 .

  • 重复 add dh, cl 每循环运行1次 .

  • 重复 add dh, dh 每循环运行1次 .

  • 重复 add dh, ch 每循环运行0.5次 . 读取[ABCD] H是特殊的,因为它们是"clean"(在这种情况下,RCX最近没有被修改) .

Terminology :所有这些离开AH(或DH)“ dirty ", i.e. in need of merging (with a merging uop) when the rest of the register is read (or in some other cases). i.e. that AH is renamed separately from RAX, if I'm understanding this correctly. " clean ”是相反的 . 有很多方法可以清理脏寄存器,最简单的方法是 inc eaxmov eax, esi .

Writing to AL only :这些循环确实从LSD运行: uops_issue.any ~ = lsd.uops .

  • 重复 mov al, bl 每循环运行1次 . 每个组偶尔会发生一次破坏 xor eax,eax ,这使得OOO执行瓶颈的uop吞吐量,而不是延迟 .

  • 重复 mov al, [rsi] 每循环运行1次,作为微融合ALU加载uop . (uops_issued = 4G循环开销,uops_executed = 8G循环开销) . 在一组4之前,一个破坏性的 xor eax,eax 使每个时钟的2个负载成为瓶颈 .

  • 重复 mov al, 123 每循环运行1次 .

  • 重复 mov al, bh 每循环运行0.5次 . (每2个循环1个) . 阅读[ABCD] H很特别 .

  • xor eax,eax 6x mov al,bh dec ebp/jnz :每个2c,前端每个时钟4个uop的瓶颈 .

  • 重复 add dl, ch 每循环运行0.5次 . (每2个循环1个) . 读[ABCD] H显然会为 dl 创造额外的延迟 .

  • 重复 add dl, cl 每循环运行1次 .

我认为对低8寄存器的写操作就像RMW混合到完整的reg中一样,就像 add eax, 123 一样,但如果 ah 是脏的,它不会触发合并 . 因此(除了忽略 AH 合并),它的行为与完全不进行部分注册重命名的CPU的行为相同 . 似乎 AL 永远不会与 RAX 分开重命名?

  • inc al / inc ah 对可以并行运行 .

  • mov ecx, eax 如果 ah 是"dirty",则插入合并的uop,但重命名实际的 mov . 这就是IvyBridge及其后的Agner Fog describes .

  • 重复 movzx eax, ah 每2个循环运行一次 . (在写完整个reg之后读取高8位寄存器会产生额外的延迟 . )

  • movzx ecx, al 具有零延迟,并且没有重命名movzx) .

  • movzx ecx, cl 具有1c延迟并占用执行端口 . (mov-elimination never works for the same,same case,仅在不同的架构寄存器之间 . )

每次迭代插入合并uop的循环都无法从LSD(循环缓冲区)运行?

我对AL / AH / RAX与B *,C *,DL / DH / RDX有什么特别之处 . 我已经在其他寄存器中测试了一些部分寄存器(尽管为了一致性我主要显示 AL / AH ),并且从未发现任何差异 .

How can we explain all of these observations with a sensible model of how the microarch works internally?


相关:部分 flag 问题与部分 register 问题不同 . 有关INC instruction vs ADD 1: Does it matter?的一些非常奇怪的东西,请参阅INC instruction vs ADD 1: Does it matter?(在Core2 / Nehalem上甚至是 shr r32,2 :不要读取除1之外的移位标记) .

有关 adc 中的部分标记内容,另请参阅Problems with ADC/SBB and INC/DEC in tight loops on some CPUs循环 .

1 回答

  • 18

    其他答案欢迎更详细地介绍Sandybridge和IvyBridge . 我无法访问该硬件 .


    我没有发现HSW和SKL之间存在任何部分注册行为差异 . 在Haswell和Skylake上,到目前为止我测试的所有内容都支持这个模型:

    AL is never renamed separately from RAX (或r15的r15b) . 因此,如果您从未触摸过high8寄存器(AH / BH / CH / DH),那么一切都与没有部分注册重命名的CPU(例如AMD)完全相同 .

    对AL的只写访问权限合并到RAX中,并依赖于RAX . 对于加载到AL的负载,这是一个微融合的ALU加载uop,它在p0156上执行,这是它在每次写入时真正合并的最有力的证据之一,而不仅仅是像Agner推测的那样进行一些花哨的双重记录 .

    Agner(和英特尔)表示,Sandybridge可能需要为AL合并uop,因此它可能会与RAX分开重命名 . 对于SnB,Intel's optimization manual (section 3.5.2.4 Partial Register Stalls)

    SnB(不一定是后来的搜索)在以下情况下插入合并的uop:在写入寄存器AH,BH,CH或DH之一之后,在随后读取2字节,4字节或8字节形式的同一个登记册 . 在这些情况下,插入合并微操作 . 插入消耗完整的分配周期,其中不能分配其他微操作 . 在具有1或2个字节的目标寄存器的微操作之后,该寄存器不是指令的源(或寄存器的更大形式),并且在随后读取2字节,4字节或8字节形式的相同的注册 . 在这些情况下,合并微操作是流程的一部分 .

    我认为他们说在SnB上, add al,bl 将RMW完整的RAX而不是单独重命名,因为其中一个源寄存器是(部分)RAX . 我的猜测是,这不适用于像 mov al, [rbx + rax] 这样的负载;在寻址模式下 rax 可能不算作源 .

    我还没有测试过high8合并uops是否仍然需要在HSW / SKL上自行发布/重命名 . 这将使前端影响相当于4 uops(因为这是问题/重命名管道宽度) .

    • 如果不编写EAX / RAX,就无法打破涉及AL的依赖关系 . xor al,al 没有帮助, mov al, 0 也没有 .

    • movzx ebx, al has zero latency (renamed), and needs no execution unit. (即,移动消除适用于HSW和SKL) . It triggers merging of AH if it's dirty ,我想这是没有ALU工作所必需的 . 它是微型导游在这里有一个错误,他说在HSW或SKL上没有消除零扩展移动,只有IvB . )

    • movzx eax, al 在重命名时未被删除 . 英特尔的mov-elimination永远不会同样适用 . mov rax,rax isn 't eliminated either, even though it doesn' t必须对任何事物进行零扩展 . (虽然'd be no point to giving it special hardware support, because it'只是一个无操作,不像 mov eax,eax ) . 无论如何,当零扩展时,更喜欢在两个独立的架构寄存器之间移动,无论是32位 mov 还是8位 movzx .
      在HSW或SKL重命名时,

    • movzx eax, bx 未被删除 . 它具有1c延迟并使用ALU uop . 英特尔的优化手册仅提到了8位movzx的零延迟(并指出 movzx r32, high8 永远不会重命名) .


    High-8 regs可以与寄存器的其余部分分开重命名,并且需要合并uop .

    • ah 的只读访问权限 mov ah, r8mov ah, [mem] 重命名AH,不依赖于旧值 . 这些都是通常不需要ALU uop的指令(对于32位版本) .

    • AH的RMW(如 inc ah )弄脏了它 .

    • setcc ah 取决于旧的 ah ,但仍然会弄脏它 . 我认为 mov ah, imm8 是相同的,但没有测试过多的角落情况 .

    (原因不明:涉及 setcc ah 的循环有时可以从LSD运行,请参阅本文末尾的 rcr 循环 . 也许只要 ah 在循环结束时是干净的,它可以使用LSD吗?) .

    如果 ah 是脏的, setcc ah 将合并到重命名的 ah 中,而不是强制合并到 rax . 例如 %rep 4inc al / test ebx,ebx / setcc ah / inc al / inc ah )不会生成合并的uops,并且仅在大约8.7c内运行(由于 ah 的uops资源冲突而导致延迟8 inc al 减速 . 此外 inc ah / setcc ah dep链) .

    我认为这里发生的事情是 setcc r8 总是被实现为读 - 修改 - 写 . 英特尔可能认为不值得使用只写 setcc uop来优化 setcc ah 情况,因为编译器生成的代码非常罕见 setcc ah . (但请参阅问题中的godbolt链接:clang4.0 with -m32 会这样做 . )

    • 读取AX,EAX或RAX会触发合并uop(占用前端问题/重命名带宽) . 可能RAT(寄存器分配表)跟踪架构R [ABCD] X的高8脏状态,甚至在写入AH退出之后,AH数据也存储在与RAX不同的物理寄存器中 . 即使在编写AH和读取EAX之间有256个NOP,也有一个额外的合并uop . (SKL上的ROB大小= 224,因此这可以保证 mov ah, 123 已经退役) . 使用uops_issued /执行的perf计数器检测到,这清楚地显示了差异 .

    • AL的读 - 修改 - 写(例如 inc al )免费合并,作为ALU uop的一部分 . (仅使用一些简单的uops进行测试,例如 add / inc ,而不是 div r8mul r8 ) . 同样,即使AH很脏,也不会触发合并的uop .

    • 只写EAX / RAX(如 lea eax, [rsi + rcx]xor eax,eax)清除AH脏状态(无合并uop) .

    • 只写AX( mov ax, 1 )首先触发AH的合并 . 我想这不是特殊套管,而是像任何其他RMW AX / RAX一样运行 . (TODO:测试 mov ax, bx ,虽然不应该重命名't be special because it' . )

    • xor ah,ah 具有1c延迟,不是dep-breaking,仍然需要执行端口 .

    • 读取和/或写入AL不会强制合并,因此AH可以保持脏(并且可以在单独的dep链中独立使用) . (例如 add ah, cl / add al, dl 可以每时钟运行1次(加密等待时间瓶颈) .


    Making AH dirty prevents a loop from running from the LSD (循环缓冲区),即使没有合并的uops . LSD是指CPU在队列中循环uops以提供问题/重命名阶段 . (称为IDQ) .

    插入合并的uops有点像为堆栈引擎插入堆栈同步uops . 英特尔's optimization manual says that SnB'的LSD无法运行具有不匹配的循环 push / pop ,这是有道理的,但它意味着它可以运行具有 balancer push / pop 的循环 . 那个's not what I'在SKL上看到:甚至 balancer push / pop 阻止从LSD运行(例如 push rax / pop rdx / times 6 imul rax, rdx . (SnB的LSD和HSW / SKL之间可能存在真正的区别:SnB may just "lock down" the uops in the IDQ instead of repeating them multiple times, so a 5-uop loop takes 2 cycles to issue instead of 1.25 . )无论如何,似乎HSW /当高8寄存器变脏或者包含堆栈引擎微操作时,SKL不能使用LSD .

    此行为可能与an erratum in SKL有关:

    SKL150:使用AH / BH / CH / DH寄存器的短循环可能导致不可预测的系统行为问题:在复杂的微架构条件下,使用AH,BH,CH或DH寄存器的小于64条指令的短循环以及它们相应的更宽的寄存器(例如,用于AH的RAX,EAX或AX)可能导致不可预测的系统行为 . 这只能在同一物理处理器上的两个逻辑处理器都处于活动状态时发生 .

    这也可能与英特尔的优化手册声明有关,即SnB至少必须在一个循环中自行发布/重命名AH合并uop . 这对于前端而言是一个奇怪的区别 .

    我的Linux内核日志说 microcode: sig=0x506e3, pf=0x2, revision=0x84 . Arch Linux的 intel-ucode 包只提供更新,you have to edit config files to actually have it loaded . 所以 my Skylake testing was on an i7-6700k with microcode revision 0x84, which doesn't include the fix for SKL150 . 在我测试的每一个案例中,它都符合Haswell的行为,IIRC . (例如,Haswell和我的SKL都可以从LSD运行 setne ah / add ah,ah / rcr ebx,1 / mov eax,ebx 循环) . 我启用了HT(这是SKL150显示的前提条件),但我正在测试一个主要是空闲的系统,所以我的线程有自己的核心 .

    使用更新的微码,LSD完全禁用所有时间,而不仅仅是部分寄存器处于活动状态 . lsd.uops 始终为零,包括真正的程序而不是合成循环 . 硬件错误(而不是微码错误)通常需要禁用整个功能来修复 . 这就是为什么SKL-avx512(SKX)是reported to not have a loopback buffer . 幸运的是,这不是性能问题:SKL在Broadwell上的uop-cache吞吐量增加几乎总能跟上问题/重命名 .


    额外的AH / BH / CH / DH潜伏期:

    • 当它不脏时(单独重命名)读取AH会为两个操作数增加额外的延迟周期 . 例如 add bl, ah 从输入BL到输出BL的延迟为2c,因此即使RAX和AH不属于它,它也会增加关键路径的延迟 . (之前我已经看到了另一个操作数的这种额外延迟,在Skylake上有矢量延迟,其中一个int / float延迟"pollutes"永远是一个寄存器.TODO:写出来 . )

    这意味着使用 movzx ecx, al / movzx edx, ah 解压缩字节与 movzx / shr eax,8 / movzx 相比具有额外的延迟,但仍然更好吞吐量 .

    • 在脏时读取AH不会增加任何延迟 . ( add ah,ahadd ah,dh / add dh,ah 每次添加有1c延迟) . 在很多角落里,我没有做过很多测试来证实这一点 .

    Hypothesis: a dirty high8 value is stored in the bottom of a physical register . 读取干净的高电平8需要移位来提取位[15:8],但读取脏的高电平8只能取物理寄存器的位[7:0],就像正常的8位寄存器读取一样 .

    额外延迟并不意味着吞吐量降低 . 即使所有 add 指令都有2c延迟(来自读取DH,未经修改),此程序每2个时钟运行1 iter .

    global _start
    _start:
        mov     ebp, 100000000
    .loop:
        add ah, dh
        add bh, dh
        add ch, dh
        add al, dh
        add bl, dh
        add cl, dh
        add dl, dh
    
        dec ebp
        jnz .loop
    
        xor edi,edi
        mov eax,231   ; __NR_exit_group  from /usr/include/asm/unistd_64.h
        syscall       ; sys_exit_group(0)
    
    Performance counter stats for './testloop':
    
         48.943652      task-clock (msec)         #    0.997 CPUs utilized          
                 1      context-switches          #    0.020 K/sec                  
                 0      cpu-migrations            #    0.000 K/sec                  
                 3      page-faults               #    0.061 K/sec                  
       200,314,806      cycles                    #    4.093 GHz                    
       100,024,930      branches                  # 2043.675 M/sec                  
       900,136,527      instructions              #    4.49  insn per cycle         
       800,219,617      uops_issued_any           # 16349.814 M/sec                 
       800,219,014      uops_executed_thread      # 16349.802 M/sec                 
             1,903      lsd_uops                  #    0.039 M/sec                  
    
       0.049107358 seconds time elapsed
    

    Some interesting test loop bodies

    %if 1
         imul eax,eax
         mov  dh, al
         inc dh
         inc dh
         inc dh
    ;     add al, dl
        mov cl,dl
        movzx eax,cl
    %endif
    
    Runs at ~2.35c per iteration on both HSW and SKL.  reading `dl` has no dep on the `inc dh` result.  But using `movzx eax, dl` instead of `mov cl,dl` / `movzx eax,cl` causes a partial-register merge, and creates a loop-carried dep chain.  (8c per iteration).
    
    
    %if 1
        imul  eax, eax
        imul  eax, eax
        imul  eax, eax
        imul  eax, eax
        imul  eax, eax         ; off the critical path unless there's a false dep
    
      %if 1
        test  ebx, ebx          ; independent of the imul results
        ;mov   ah, 123         ; dependent on RAX
        ;mov  eax,0           ; breaks the RAX dependency
        setz  ah              ; dependent on RAX
      %else
        mov   ah, bl          ; dep-breaking
      %endif
    
        add   ah, ah
        ;; ;inc   eax
    ;    sbb   eax,eax
    
        rcr   ebx, 1      ; dep on  add ah,ah  via CF
        mov   eax,ebx     ; clear AH-dirty
    
        ;; mov   [rdi], ah
        ;; movzx eax, byte [rdi]   ; clear AH-dirty, and remove dep on old value of RAX
        ;; add   ebx, eax          ; make the dep chain through AH loop-carried
    %endif
    

    setcc版本(带有 %if 1 )具有20c循环传输延迟,并且即使它具有 setcc ahadd ah,ah ,也从LSD运行 .

    00000000004000e0 <_start.loop>:
      4000e0:       0f af c0                imul   eax,eax
      4000e3:       0f af c0                imul   eax,eax
      4000e6:       0f af c0                imul   eax,eax
      4000e9:       0f af c0                imul   eax,eax
      4000ec:       0f af c0                imul   eax,eax
      4000ef:       85 db                   test   ebx,ebx
      4000f1:       0f 94 d4                sete   ah
      4000f4:       00 e4                   add    ah,ah
      4000f6:       d1 db                   rcr    ebx,1
      4000f8:       89 d8                   mov    eax,ebx
      4000fa:       ff cd                   dec    ebp
      4000fc:       75 e2                   jne    4000e0 <_start.loop>
    
     Performance counter stats for './testloop' (4 runs):
    
           4565.851575      task-clock (msec)         #    1.000 CPUs utilized            ( +-  0.08% )
                     4      context-switches          #    0.001 K/sec                    ( +-  5.88% )
                     0      cpu-migrations            #    0.000 K/sec                  
                     3      page-faults               #    0.001 K/sec                  
        20,007,739,240      cycles                    #    4.382 GHz                      ( +-  0.00% )
         1,001,181,788      branches                  #  219.276 M/sec                    ( +-  0.00% )
        12,006,455,028      instructions              #    0.60  insn per cycle           ( +-  0.00% )
        13,009,415,501      uops_issued_any           # 2849.286 M/sec                    ( +-  0.00% )
        12,009,592,328      uops_executed_thread      # 2630.307 M/sec                    ( +-  0.00% )
        13,055,852,774      lsd_uops                  # 2859.456 M/sec                    ( +-  0.29% )
    
           4.565914158 seconds time elapsed                                          ( +-  0.08% )
    

    不明原因:它从LSD运行,即使它使AH变脏 . (至少我认为确实如此.TODO:尝试在 mov eax,ebx 清除它之前添加一些与 eax 做某事的指令 . )

    但是对于 mov ah, bl ,它在HSW / SKL上每次迭代运行5.0c( imul 吞吐量瓶颈) . (已注释掉的商店/重装也有效,但SKL的存储转发速度比HSW快,而且variable-latency ......)

    #  mov ah, bl   version
     5,009,785,393      cycles                    #    4.289 GHz                      ( +-  0.08% )
     1,000,315,930      branches                  #  856.373 M/sec                    ( +-  0.00% )
    11,001,728,338      instructions              #    2.20  insn per cycle           ( +-  0.00% )
    12,003,003,708      uops_issued_any           # 10275.807 M/sec                   ( +-  0.00% )
    11,002,974,066      uops_executed_thread      # 9419.678 M/sec                    ( +-  0.00% )
             1,806      lsd_uops                  #    0.002 M/sec                    ( +-  3.88% )
    
       1.168238322 seconds time elapsed                                          ( +-  0.33% )
    

    请注意,它不再从LSD运行 .

相关问题