在英特尔 Broadwell 处理器中存在重大的 FMA 性能异常。

36
  • 代码1:

    vzeroall
    mov             rcx, 1000000
    startLabel1:
    vfmadd231ps     ymm0, ymm0, ymm0
    vfmadd231ps     ymm1, ymm1, ymm1
    vfmadd231ps     ymm2, ymm2, ymm2
    vfmadd231ps     ymm3, ymm3, ymm3
    vfmadd231ps     ymm4, ymm4, ymm4
    vfmadd231ps     ymm5, ymm5, ymm5
    vfmadd231ps     ymm6, ymm6, ymm6
    vfmadd231ps     ymm7, ymm7, ymm7
    vfmadd231ps     ymm8, ymm8, ymm8
    vfmadd231ps     ymm9, ymm9, ymm9
    vpaddd          ymm10, ymm10, ymm10
    vpaddd          ymm11, ymm11, ymm11
    vpaddd          ymm12, ymm12, ymm12
    vpaddd          ymm13, ymm13, ymm13
    vpaddd          ymm14, ymm14, ymm14
    dec             rcx
    jnz             startLabel1
    
  • Code2:

    vzeroall
    mov             rcx, 1000000
    startLabel2:
    vmulps          ymm0, ymm0, ymm0
    vmulps          ymm1, ymm1, ymm1
    vmulps          ymm2, ymm2, ymm2
    vmulps          ymm3, ymm3, ymm3
    vmulps          ymm4, ymm4, ymm4
    vmulps          ymm5, ymm5, ymm5
    vmulps          ymm6, ymm6, ymm6
    vmulps          ymm7, ymm7, ymm7
    vmulps          ymm8, ymm8, ymm8
    vmulps          ymm9, ymm9, ymm9
    vpaddd          ymm10, ymm10, ymm10
    vpaddd          ymm11, ymm11, ymm11
    vpaddd          ymm12, ymm12, ymm12
    vpaddd          ymm13, ymm13, ymm13
    vpaddd          ymm14, ymm14, ymm14
    dec             rcx
    jnz             startLabel2
    
  • 代码3(与代码2相同,但具有较长的VEX前缀):

  • vzeroall
    mov             rcx, 1000000
    startLabel3:
    byte            0c4h, 0c1h, 07ch, 059h, 0c0h ;long VEX form vmulps ymm0, ymm0, ymm0
    byte            0c4h, 0c1h, 074h, 059h, 0c9h ;long VEX form vmulps ymm1, ymm1, ymm1
    byte            0c4h, 0c1h, 06ch, 059h, 0d2h ;long VEX form vmulps ymm2, ymm2, ymm2
    byte            0c4h, 0c1h, 06ch, 059h, 0dbh ;long VEX form vmulps ymm3, ymm3, ymm3
    byte            0c4h, 0c1h, 05ch, 059h, 0e4h ;long VEX form vmulps ymm4, ymm4, ymm4
    byte            0c4h, 0c1h, 054h, 059h, 0edh ;long VEX form vmulps ymm5, ymm5, ymm5
    byte            0c4h, 0c1h, 04ch, 059h, 0f6h ;long VEX form vmulps ymm6, ymm6, ymm6
    byte            0c4h, 0c1h, 044h, 059h, 0ffh ;long VEX form vmulps ymm7, ymm7, ymm7
    vmulps          ymm8, ymm8, ymm8
    vmulps          ymm9, ymm9, ymm9
    vpaddd          ymm10, ymm10, ymm10
    vpaddd          ymm11, ymm11, ymm11
    vpaddd          ymm12, ymm12, ymm12
    vpaddd          ymm13, ymm13, ymm13
    vpaddd          ymm14, ymm14, ymm14
    dec             rcx
    jnz             startLabel3
    
  • Code4(与Code1相同,但使用xmm寄存器):

  • vzeroall
    mov             rcx, 1000000
    startLabel4:
    vfmadd231ps     xmm0, xmm0, xmm0
    vfmadd231ps     xmm1, xmm1, xmm1
    vfmadd231ps     xmm2, xmm2, xmm2
    vfmadd231ps     xmm3, xmm3, xmm3
    vfmadd231ps     xmm4, xmm4, xmm4
    vfmadd231ps     xmm5, xmm5, xmm5
    vfmadd231ps     xmm6, xmm6, xmm6
    vfmadd231ps     xmm7, xmm7, xmm7
    vfmadd231ps     xmm8, xmm8, xmm8
    vfmadd231ps     xmm9, xmm9, xmm9
    vpaddd          xmm10, xmm10, xmm10
    vpaddd          xmm11, xmm11, xmm11
    vpaddd          xmm12, xmm12, xmm12
    vpaddd          xmm13, xmm13, xmm13
    vpaddd          xmm14, xmm14, xmm14
    dec             rcx
    jnz             startLabel4
    
  • Code5(与Code1相同,但使用vpsubd的非零值):

  • vzeroall
    mov             rcx, 1000000
    startLabel5:
    vfmadd231ps     ymm0, ymm0, ymm0
    vfmadd231ps     ymm1, ymm1, ymm1
    vfmadd231ps     ymm2, ymm2, ymm2
    vfmadd231ps     ymm3, ymm3, ymm3
    vfmadd231ps     ymm4, ymm4, ymm4
    vfmadd231ps     ymm5, ymm5, ymm5
    vfmadd231ps     ymm6, ymm6, ymm6
    vfmadd231ps     ymm7, ymm7, ymm7
    vfmadd231ps     ymm8, ymm8, ymm8
    vfmadd231ps     ymm9, ymm9, ymm9
    vpsubd          ymm10, ymm10, ymm11
    vpsubd          ymm11, ymm11, ymm12
    vpsubd          ymm12, ymm12, ymm13
    vpsubd          ymm13, ymm13, ymm14
    vpsubd          ymm14, ymm14, ymm10
    dec             rcx
    jnz             startLabel5
    
  • Code6b:(修改,仅适用于vpaddds的内存操作数)

  • vzeroall
    mov             rcx, 1000000
    startLabel6:
    vfmadd231ps     ymm0, ymm0, ymm0
    vfmadd231ps     ymm1, ymm1, ymm1
    vfmadd231ps     ymm2, ymm2, ymm2
    vfmadd231ps     ymm3, ymm3, ymm3
    vfmadd231ps     ymm4, ymm4, ymm4
    vfmadd231ps     ymm5, ymm5, ymm5
    vfmadd231ps     ymm6, ymm6, ymm6
    vfmadd231ps     ymm7, ymm7, ymm7
    vfmadd231ps     ymm8, ymm8, ymm8
    vfmadd231ps     ymm9, ymm9, ymm9
    vpaddd          ymm10, ymm10, [mem]
    vpaddd          ymm11, ymm11, [mem]
    vpaddd          ymm12, ymm12, [mem]
    vpaddd          ymm13, ymm13, [mem]
    vpaddd          ymm14, ymm14, [mem]
    dec             rcx
    jnz             startLabel6
    
  • 代码7: (与代码1相同,但vpaddds使用ymm15)

  • vzeroall
    mov             rcx, 1000000
    startLabel7:
    vfmadd231ps     ymm0, ymm0, ymm0
    vfmadd231ps     ymm1, ymm1, ymm1
    vfmadd231ps     ymm2, ymm2, ymm2
    vfmadd231ps     ymm3, ymm3, ymm3
    vfmadd231ps     ymm4, ymm4, ymm4
    vfmadd231ps     ymm5, ymm5, ymm5
    vfmadd231ps     ymm6, ymm6, ymm6
    vfmadd231ps     ymm7, ymm7, ymm7
    vfmadd231ps     ymm8, ymm8, ymm8
    vfmadd231ps     ymm9, ymm9, ymm9
    vpaddd          ymm10, ymm15, ymm15
    vpaddd          ymm11, ymm15, ymm15
    vpaddd          ymm12, ymm15, ymm15
    vpaddd          ymm13, ymm15, ymm15
    vpaddd          ymm14, ymm15, ymm15
    dec             rcx
    jnz             startLabel7
    
  • Code8:(与Code7相同,但使用xmm而不是ymm)

  • vzeroall
    mov             rcx, 1000000
    startLabel8:
    vfmadd231ps     xmm0, ymm0, ymm0
    vfmadd231ps     xmm1, xmm1, xmm1
    vfmadd231ps     xmm2, xmm2, xmm2
    vfmadd231ps     xmm3, xmm3, xmm3
    vfmadd231ps     xmm4, xmm4, xmm4
    vfmadd231ps     xmm5, xmm5, xmm5
    vfmadd231ps     xmm6, xmm6, xmm6
    vfmadd231ps     xmm7, xmm7, xmm7
    vfmadd231ps     xmm8, xmm8, xmm8
    vfmadd231ps     xmm9, xmm9, xmm9
    vpaddd          xmm10, xmm15, xmm15
    vpaddd          xmm11, xmm15, xmm15
    vpaddd          xmm12, xmm15, xmm15
    vpaddd          xmm13, xmm15, xmm15
    vpaddd          xmm14, xmm15, xmm15
    dec             rcx
    jnz             startLabel8
    
  • 关闭Turbo和C1E后,测量TSC时钟:
  •           Haswell        Broadwell                  Skylake
    
    CPUID     306C3, 40661   306D4, 40671               506E3
    
    Code1     ~5000000        ~7730000 ->~54% slower    ~5500000 ->~10% slower
    Code2     ~5000000       ~5000000                  ~5000000
    Code3     ~6000000       ~5000000                  ~5000000
    Code4     ~5000000       ~7730000                  ~5500000
    Code5     ~5000000       ~7730000                  ~5500000
    Code6b    ~5000000       ~8380000                  ~5500000
    Code7     ~5000000       ~5000000                  ~5000000
    Code8     ~5000000       ~5000000                  ~5000000 
    
    1. Code1在Broadwell上会发生什么?我的猜测是,对于Code1情况,Broadwell会以某种方式污染Port1与vpaddds,然而Haswell只能使用Port5,如果Port0和Port1已满。

    2. 您有任何想法如何在Broadwell上使用FMA指令实现~5000000 clk吗?

    3. 我尝试重新排序。双精度和qword经历了类似的行为;

    4. 我使用了Windows 8.1和Win 10;

      更新:


    5. 添加了Code3作为Marat Dukhan的长VEX思路;

    6. 扩展了Skylake经验的结果表;

    7. 上传了一个VS2015 Community + MASM示例代码here

      更新2:


    8. 我尝试使用xmm寄存器代替ymm(Code 4)。Broadwell上的结果相同。

      更新3:


    9. 我添加了Code5作为Peter Cordes的思路(使用其他指令(vpxor,vpor,vpand,vpandn,vpsubd)替换vpaddd)。如果新指令不是零元算符(vpxor,vpsubd与同一寄存器),则在BDW上结果相同。已使用Code4和Code5更新示例项目。

      更新4:


    10. 我添加了Code6作为Stephen Canon的思路(内存操作数)。结果是~8200000 clks。使用Code6更新示例项目;

    11. 我检查了CPU频率和AIDA64系统稳定测试中的可能抑制。频率稳定,没有抑制迹象;

      enter image description here

    12. Intel IACA 2.1 Haswell吞吐量分析:

      Intel(R) Architecture Code Analyzer Version - 2.1
      Analyzed File - Assembly.obj
      Binary Format - 64Bit
      Architecture  - HSW
      Analysis Type - Throughput
      
      Throughput Analysis Report
      --------------------------
      Block Throughput: 5.10 Cycles       Throughput Bottleneck: Port0, Port1, Port5
      
      Port Binding In Cycles Per Iteration:
      ---------------------------------------------------------------------------------------
      |  Port  |  0   -  DV  |  1   |  2   -  D   |  3   -  D   |  4   |  5   |  6   |  7   |
      ---------------------------------------------------------------------------------------
      | Cycles | 5.0    0.0  | 5.0  | 0.0    0.0  | 0.0    0.0  | 0.0  | 5.0  | 1.0  | 0.0  |
      ---------------------------------------------------------------------------------------
      
      | Num Of |                    Ports pressure in cycles                     |    |
      |  Uops  |  0  - DV  |  1  |  2  -  D  |  3  -  D  |  4  |  5  |  6  |  7  |    |
      ---------------------------------------------------------------------------------
      |   1    | 1.0       |     |           |           |     |     |     |     | CP | vfmadd231ps ymm0, ymm0, ymm0
      |   1    |           | 1.0 |           |           |     |     |     |     | CP | vfmadd231ps ymm1, ymm1, ymm1
      |   1    | 1.0       |     |           |           |     |     |     |     | CP | vfmadd231ps ymm2, ymm2, ymm2
      |   1    |           | 1.0 |           |           |     |     |     |     | CP | vfmadd231ps ymm3, ymm3, ymm3
      |   1    | 1.0       |     |           |           |     |     |     |     | CP | vfmadd231ps ymm4, ymm4, ymm4
      |   1    |           | 1.0 |           |           |     |     |     |     | CP | vfmadd231ps ymm5, ymm5, ymm5
      |   1    | 1.0       |     |           |           |     |     |     |     | CP | vfmadd231ps ymm6, ymm6, ymm6
      |   1    |           | 1.0 |           |           |     |     |     |     | CP | vfmadd231ps ymm7, ymm7, ymm7
      |   1    | 1.0       |     |           |           |     |     |     |     | CP | vfmadd231ps ymm8, ymm8, ymm8
      |   1    |           | 1.0 |           |           |     |     |     |     | CP | vfmadd231ps ymm9, ymm9, ymm9
      |   1    |           |     |           |           |     | 1.0 |     |     | CP | vpaddd ymm10, ymm10, ymm10
      |   1    |           |     |           |           |     | 1.0 |     |     | CP | vpaddd ymm11, ymm11, ymm11
      |   1    |           |     |           |           |     | 1.0 |     |     | CP | vpaddd ymm12, ymm12, ymm12
      |   1    |           |     |           |           |     | 1.0 |     |     | CP | vpaddd ymm13, ymm13, ymm13
      |   1    |           |     |           |           |     | 1.0 |     |     | CP | vpaddd ymm14, ymm14, ymm14
      |   1    |           |     |           |           |     |     | 1.0 |     |    | dec rcx
      |   0F   |           |     |           |           |     |     |     |     |    | jnz 0xffffffffffffffaa
      Total Num Of Uops: 16
      
      我跟随 jcomeau_ictx 的想法,修改了 Agner Fog 发布于2015年12月22日的 testp.zip 文件。 在BDW 306D4上使用的端口:
    13.            Clock   Core cyc   Instruct      uop p0     uop p1     uop p5     uop p6 
      Code1:   7734720    7734727   17000001    4983410    5016592    5000001    1000001
      Code2:   5000072    5000072   17000001    5000010    5000014    4999978    1000002
      

      端口分配与Haswell基本一致。然后我检查了资源停顿计数器(事件0xa2)。

                Clock   Core cyc   Instruct      res.stl.   RS stl.    SB stl.    ROB stl.
      Code1:   7736212    7736213   17000001    3736191    3736143          0          0
      Code2:   5000068    5000072   17000001    1000050     999957          0          0
      

      据我看来,Code1和Code2的差别似乎是由于RS停滞引起的。Intel SDM中的注释是:“由于没有符合要求的RS条目可用而导致的周期停滞。”

      如何使用FMA避免这种停滞?

      更新5:


    14. 根据Peter Cordes的提醒,Code6已更改,只有vpaddds使用内存操作数。对HSW和SKL没有影响,对BDW更糟。

    15. 根据Marat Dukhan的测量结果,不仅vpadd/vpsub/vpand/vpandn/vpxor受到影响,还有其他Port5受限指令,如vmovaps、vblendps、vpermps、vshufps、vbroadcastss;

    16. 正如IwillnotexistIdonotexist所建议的那样,我尝试使用其他操作数。成功修改的版本是Code7,其中所有vpaddds均使用ymm15。这个版本在BDWs上可以产生约5000000个时钟周期,但仅持续了一段时间。经过约600万个FMA对后,它达到了通常的约7730000个时钟周期:

    17. Clock   Core cyc   Instruct   res.stl.   RS stl.     SB stl.    ROB stl.
      5133724    5110723   17000001    1107998     946376          0          0
      6545476    6545482   17000001    2545453          1          0          0
      6545468    6545471   17000001    2545437      90910          0          0
      5000016    5000019   17000001     999992     999992          0          0
      7671620    7617127   17000003    3614464    3363363          0          0
      7737340    7737345   17000001    3737321    3737259          0          0
      7802916    7747108   17000003    3737478    3735919          0          0
      7928784    7796057   17000007    3767962    3676744          0          0
      7941072    7847463   17000003    3781103    3651595          0          0
      7787812    7779151   17000005    3765109    3685600          0          0
      7792524    7738029   17000002    3736858    3736764          0          0
      7736000    7736007   17000001    3735983    3735945          0          0
      
    18. 我尝试了Code7的xmm版本,就像Code8一样。效果类似,但更快的运行时间持久更长。我没有发现1.6GHz i5-5250U和3.7GHz i7-5775C之间有显着的差异。

    19. 禁用HyperThreading制作了16和17。启用HTT效果较差。


1
一个区别是 VFMADD231PS ymm0,ymm0,ymm0 是一个 5 字节的指令(3 字节的 VEX 前缀),而 VMULPS ymm0,ymm0,ymm0 是一个 4 字节的指令(2 字节的 VEX 前缀)。你确定问题不是由于 ifetch/decoder 引起的吗? - Marat Dukhan
1
谢谢您的想法。我用了long-VEX vmulps尝试了一下:在HSW上大约需要6000000个时钟周期,在BDW上需要5000000个时钟周期。我认为这个循环适合LSD队列。 - User9973
3
我已经为你的问题添加了悬赏。希望这能吸引更多人的关注。我本想加50分以上,但我认为许多人并没有Broadwell硬件。你能否请提供每个处理器正在测试的确切硬件信息? - Z boson
3
Agner Fog在Skylake上观察到256位操作需要约56000个时钟周期的预热时间(请参阅他于2015年12月23日发布的最新手册)。其他人也在Sandy Bridge和Haswell上观察到类似的效果(但他没有)。由于您运行了5000000个周期,因此56000个周期仅产生约1%的影响,但这值得考虑。由于您首先运行fma测试,因此它会受到影响。但显然,并不是所有处理器都会受到影响。可能只有高端处理器会关闭上128位。 - Z boson
1
@PeterCordes:谢谢你的想法。在BDW 306D4 FMA + VPADDD,VPOR,VPAND,VPANDN ~7730000个时钟周期内,FMA + VPXOR,VPSUBD ~5000000个时钟周期内。目前我无法访问其他配置。 - User9973
显示剩余53条评论
2个回答

17

更新

我对于你的问题没有解释,因为我使用的是Haswell处理器,但是我有一些代码可以分享,这可能有助于你或其他使用Broadwell或Skylake硬件的人隔离你的问题。如果你能在你的机器上运行它并分享结果,我们就可以了解你的机器发生了什么。

介绍

近期的英特尔Core i7处理器具有7个性能监视器计数器(PMC),其中3个固定功能和4个通用目的,可用于对代码进行分析。固定功能PMCs包括:

  • 指令退役
  • 未暂停的核心周期(包括TurboBoost效应的时钟周期)
  • 未暂停的参考周期(固定频率的时钟周期)

核心:参考时钟周期的比率确定了由动态频率缩放引起的相对加速或减速。

尽管存在软件(请参见下面的评论)可以访问这些计数器,但我不知道它们,而且仍然认为它们不够细粒度。

因此,我写了一个Linux内核模块perfcount,在过去的几天里授予我访问英特尔性能计数器监视器的权限,并为您的代码编写了一个用户空间测试平台和库,将您的FMA代码包装在我的LKM调用中。如何重现我的设置的说明将随后提供。

我的测试平台源代码如下。它先进行预热,然后运行你的代码多次,在长列表的指标上测试它。我将你的循环次数改为10亿次。因为同时只能编程4个通用PMC,所以我每次进行4个测量。

perfcountdemo.c

/* Includes */
#include "libperfcount.h"
#include <ctype.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


/* Function prototypes */
void code1(void);
void code2(void);
void code3(void);
void code4(void);
void code5(void);

/* Global variables */
void ((*FN_TABLE[])(void)) = {
    code1,
    code2,
    code3,
    code4,
    code5
};


/**
 * Code snippets to bench
 */

void code1(void){
    asm volatile(
    ".intel_syntax noprefix\n\t"
    "vzeroall\n\t"
    "mov             rcx, 1000000000\n\t"
    "LstartLabel1:\n\t"
    "vfmadd231ps     %%ymm0, %%ymm0, %%ymm0\n\t"
    "vfmadd231ps     ymm1, ymm1, ymm1\n\t"
    "vfmadd231ps     ymm2, ymm2, ymm2\n\t"
    "vfmadd231ps     ymm3, ymm3, ymm3\n\t"
    "vfmadd231ps     ymm4, ymm4, ymm4\n\t"
    "vfmadd231ps     ymm5, ymm5, ymm5\n\t"
    "vfmadd231ps     ymm6, ymm6, ymm6\n\t"
    "vfmadd231ps     ymm7, ymm7, ymm7\n\t"
    "vfmadd231ps     ymm8, ymm8, ymm8\n\t"
    "vfmadd231ps     ymm9, ymm9, ymm9\n\t"
    "vpaddd          ymm10, ymm10, ymm10\n\t"
    "vpaddd          ymm11, ymm11, ymm11\n\t"
    "vpaddd          ymm12, ymm12, ymm12\n\t"
    "vpaddd          ymm13, ymm13, ymm13\n\t"
    "vpaddd          ymm14, ymm14, ymm14\n\t"
    "dec             rcx\n\t"
    "jnz             LstartLabel1\n\t"
    ".att_syntax noprefix\n\t"
    : /* No outputs we care about */
    : /* No inputs we care about */
    : "xmm0",  "xmm1",  "xmm2",  "xmm3",  "xmm4",  "xmm5",  "xmm6",  "xmm7",
      "xmm8",  "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15",
      "rcx",
      "memory"
    );
}
void code2(void){

}
void code3(void){

}
void code4(void){

}
void code5(void){

}



/* Test Schedule */
const char* const SCHEDULE[] = {
    /* Batch */
    "uops_issued.any",
    "uops_issued.any<1",
    "uops_issued.any>=1",
    "uops_issued.any>=2",
    /* Batch */
    "uops_issued.any>=3",
    "uops_issued.any>=4",
    "uops_issued.any>=5",
    "uops_issued.any>=6",
    /* Batch */
    "uops_executed_port.port_0",
    "uops_executed_port.port_1",
    "uops_executed_port.port_2",
    "uops_executed_port.port_3",
    /* Batch */
    "uops_executed_port.port_4",
    "uops_executed_port.port_5",
    "uops_executed_port.port_6",
    "uops_executed_port.port_7",
    /* Batch */
    "resource_stalls.any",
    "resource_stalls.rs",
    "resource_stalls.sb",
    "resource_stalls.rob",
    /* Batch */
    "uops_retired.all",
    "uops_retired.all<1",
    "uops_retired.all>=1",
    "uops_retired.all>=2",
    /* Batch */
    "uops_retired.all>=3",
    "uops_retired.all>=4",
    "uops_retired.all>=5",
    "uops_retired.all>=6",
    /* Batch */
    "inst_retired.any_p",
    "inst_retired.any_p<1",
    "inst_retired.any_p>=1",
    "inst_retired.any_p>=2",
    /* Batch */
    "inst_retired.any_p>=3",
    "inst_retired.any_p>=4",
    "inst_retired.any_p>=5",
    "inst_retired.any_p>=6",
    /* Batch */
    "idq_uops_not_delivered.core",
    "idq_uops_not_delivered.core<1",
    "idq_uops_not_delivered.core>=1",
    "idq_uops_not_delivered.core>=2",
    /* Batch */
    "idq_uops_not_delivered.core>=3",
    "idq_uops_not_delivered.core>=4",
    "rs_events.empty",
    "idq.empty",
    /* Batch */
    "idq.mite_all_uops",
    "idq.mite_all_uops<1",
    "idq.mite_all_uops>=1",
    "idq.mite_all_uops>=2",
    /* Batch */
    "idq.mite_all_uops>=3",
    "idq.mite_all_uops>=4",
    "move_elimination.int_not_eliminated",
    "move_elimination.simd_not_eliminated",
    /* Batch */
    "lsd.uops",
    "lsd.uops<1",
    "lsd.uops>=1",
    "lsd.uops>=2",
    /* Batch */
    "lsd.uops>=3",
    "lsd.uops>=4",
    "ild_stall.lcp",
    "ild_stall.iq_full",
    /* Batch */
    "br_inst_exec.all_branches",
    "br_inst_exec.0x81",
    "br_inst_exec.0x82",
    "icache.misses",
    /* Batch */
    "br_misp_exec.all_branches",
    "br_misp_exec.0x81",
    "br_misp_exec.0x82",
    "fp_assist.any",
    /* Batch */
    "cpu_clk_unhalted.core_clk",
    "cpu_clk_unhalted.ref_xclk",
    "baclears.any"

};
const int NUMCOUNTS = sizeof(SCHEDULE)/sizeof(*SCHEDULE);


/**
 * Main
 */

int main(int argc, char* argv[]){
    int i;

    /**
     * Initialize
     */

    pfcInit();
    if(argc <= 1){
        pfcDumpEvents();
        exit(1);
    }
    pfcPinThread(3);


    /**
     * Arguments are:
     * 
     *     perfcountdemo #codesnippet
     * 
     * There is a schedule of configuration that is followed.
     */

    void (*fn)(void) = FN_TABLE[strtoull(argv[1], NULL, 0)];
    static const uint64_t ZERO_CNT[7] = {0,0,0,0,0,0,0};
    static const uint64_t ZERO_CFG[7] = {0,0,0,0,0,0,0};

    uint64_t cnt[7]                   = {0,0,0,0,0,0,0};
    uint64_t cfg[7]                   = {2,2,2,0,0,0,0};

    /* Warmup */
    for(i=0;i<10;i++){
        fn();
    }

    /* Run master loop */
    for(i=0;i<NUMCOUNTS;i+=4){
        /* Configure counters */
        const char* sched0 = i+0 < NUMCOUNTS ? SCHEDULE[i+0] : "";
        const char* sched1 = i+1 < NUMCOUNTS ? SCHEDULE[i+1] : "";
        const char* sched2 = i+2 < NUMCOUNTS ? SCHEDULE[i+2] : "";
        const char* sched3 = i+3 < NUMCOUNTS ? SCHEDULE[i+3] : "";
        cfg[3] = pfcParseConfig(sched0);
        cfg[4] = pfcParseConfig(sched1);
        cfg[5] = pfcParseConfig(sched2);
        cfg[6] = pfcParseConfig(sched3);

        pfcWrConfigCnts(0, 7, cfg);
        pfcWrCountsCnts(0, 7, ZERO_CNT);
        pfcRdCountsCnts(0, 7, cnt);
        /* ^ Should report 0s, and launch the counters. */
        /************** Hot section **************/
        fn();
        /************ End Hot section ************/
        pfcRdCountsCnts(0, 7, cnt);
        pfcWrConfigCnts(0, 7, ZERO_CFG);
        /* ^ Should clear the counter config and disable them. */

        /**
         * Print the lovely results
         */

        printf("Instructions Issued                : %20llu\n", cnt[0]);
        printf("Unhalted core cycles               : %20llu\n", cnt[1]);
        printf("Unhalted reference cycles          : %20llu\n", cnt[2]);
        printf("%-35s: %20llu\n", sched0, cnt[3]);
        printf("%-35s: %20llu\n", sched1, cnt[4]);
        printf("%-35s: %20llu\n", sched2, cnt[5]);
        printf("%-35s: %20llu\n", sched3, cnt[6]);
    }

    /**
     * Close up shop
     */

    pfcFini();
}

在我的电脑上,我得到了以下结果:

Haswell Core i7-4700MQ

(译注:此处为CPU型号名称)
> ./perfcountdemo 0
Instructions Issued                :          17000001807
Unhalted core cycles               :           5305920785
Unhalted reference cycles          :           4245764952
uops_issued.any                    :          16000811079
uops_issued.any<1                  :           1311417889
uops_issued.any>=1                 :           4000292290
uops_issued.any>=2                 :           4000229358
Instructions Issued                :          17000001806
Unhalted core cycles               :           5303822082
Unhalted reference cycles          :           4243345896
uops_issued.any>=3                 :           4000156998
uops_issued.any>=4                 :           4000110067
uops_issued.any>=5                 :                    0
uops_issued.any>=6                 :                    0
Instructions Issued                :          17000001811
Unhalted core cycles               :           5314227923
Unhalted reference cycles          :           4252020624
uops_executed_port.port_0          :           5016261477
uops_executed_port.port_1          :           5036728509
uops_executed_port.port_2          :                 5282
uops_executed_port.port_3          :                12481
Instructions Issued                :          17000001816
Unhalted core cycles               :           5329351248
Unhalted reference cycles          :           4265809728
uops_executed_port.port_4          :                 7087
uops_executed_port.port_5          :           4946019835
uops_executed_port.port_6          :           1000228324
uops_executed_port.port_7          :                 1372
Instructions Issued                :          17000001816
Unhalted core cycles               :           5325153463
Unhalted reference cycles          :           4261060248
resource_stalls.any                :           1322734589
resource_stalls.rs                 :            844250210
resource_stalls.sb                 :                    0
resource_stalls.rob                :                    0
Instructions Issued                :          17000001814
Unhalted core cycles               :           5327823817
Unhalted reference cycles          :           4262914728
uops_retired.all                   :          16000445793
uops_retired.all<1                 :            687284798
uops_retired.all>=1                :           4646263984
uops_retired.all>=2                :           4452324050
Instructions Issued                :          17000001809
Unhalted core cycles               :           5311736558
Unhalted reference cycles          :           4250015688
uops_retired.all>=3                :           3545695253
uops_retired.all>=4                :           3341664653
uops_retired.all>=5                :                 1016
uops_retired.all>=6                :                    1
Instructions Issued                :          17000001871
Unhalted core cycles               :           5477215269
Unhalted reference cycles          :           4383891984
inst_retired.any_p                 :          17000001871
inst_retired.any_p<1               :            891904306
inst_retired.any_p>=1              :           4593972062
inst_retired.any_p>=2              :           4441024510
Instructions Issued                :          17000001835
Unhalted core cycles               :           5377202052
Unhalted reference cycles          :           4302895152
inst_retired.any_p>=3              :           3555852364
inst_retired.any_p>=4              :           3369559466
inst_retired.any_p>=5              :            999980244
inst_retired.any_p>=6              :                    0
Instructions Issued                :          17000001826
Unhalted core cycles               :           5349373678
Unhalted reference cycles          :           4280991912
idq_uops_not_delivered.core        :              1580573
idq_uops_not_delivered.core<1      :           5354931839
idq_uops_not_delivered.core>=1     :               471248
idq_uops_not_delivered.core>=2     :               418625
Instructions Issued                :          17000001808
Unhalted core cycles               :           5309687640
Unhalted reference cycles          :           4248083976
idq_uops_not_delivered.core>=3     :               280800
idq_uops_not_delivered.core>=4     :               247923
rs_events.empty                    :                    0
idq.empty                          :               649944
Instructions Issued                :          17000001838
Unhalted core cycles               :           5392229041
Unhalted reference cycles          :           4315704216
idq.mite_all_uops                  :              2496139
idq.mite_all_uops<1                :           5397877484
idq.mite_all_uops>=1               :               971582
idq.mite_all_uops>=2               :               595973
Instructions Issued                :          17000001822
Unhalted core cycles               :           5347205506
Unhalted reference cycles          :           4278845208
idq.mite_all_uops>=3               :               394011
idq.mite_all_uops>=4               :               335205
move_elimination.int_not_eliminated:                    0
move_elimination.simd_not_eliminated:                    0
Instructions Issued                :          17000001812
Unhalted core cycles               :           5320621549
Unhalted reference cycles          :           4257095280
lsd.uops                           :          15999287982
lsd.uops<1                         :           1326629729
lsd.uops>=1                        :           3999821996
lsd.uops>=2                        :           3999821996
Instructions Issued                :          17000001813
Unhalted core cycles               :           5320533147
Unhalted reference cycles          :           4257105096
lsd.uops>=3                        :           3999823498
lsd.uops>=4                        :           3999823498
ild_stall.lcp                      :                    0
ild_stall.iq_full                  :                 3468
Instructions Issued                :          17000001813
Unhalted core cycles               :           5323278281
Unhalted reference cycles          :           4258969200
br_inst_exec.all_branches          :           1000016626
br_inst_exec.0x81                  :           1000016616
br_inst_exec.0x82                  :                    0
icache.misses                      :                  294
Instructions Issued                :          17000001812
Unhalted core cycles               :           5315098728
Unhalted reference cycles          :           4253082504
br_misp_exec.all_branches          :                    5
br_misp_exec.0x81                  :                    2
br_misp_exec.0x82                  :                    0
fp_assist.any                      :                    0
Instructions Issued                :          17000001819
Unhalted core cycles               :           5338484610
Unhalted reference cycles          :           4271432976
cpu_clk_unhalted.core_clk          :           5338494250
cpu_clk_unhalted.ref_xclk          :            177976806
baclears.any                       :                    1
                                   :                    0

我们可以看到在Haswell上,一切都很顺畅。我会从上面的统计数据中做出一些注释:
  • 对于我来说,发出的指令非常一致。它总是在17000001800左右,这是一个好兆头:这意味着我们可以很好地估计我们的开销。其他固定功能计数器也是如此。它们都相当匹配,这意味着4个批次的测试是苹果到苹果的比较。
  • 核心:参考周期比率约为5305920785/4245764952,我们得到平均频率缩放约为1.25;这与我的观察相符,即我的核心从2.4 GHz升至3.0 GHz。cpu_clk_unhalted.core_clk/(10.0*cpu_clk_unhalted.ref_xclk)也给出了略低于3 GHz的结果。
  • 发出的指令与核心周期的比率给出了IPC,17000001807/5305920785约为3.20,这也是正确的:每4个时钟周期有2个FMA+1个VPADDD,每5个时钟周期有2个额外的循环控制指令并行执行。
  • uops_issued.any:发出的指令数量约为17B,但发出的uops数量约为16B。这是因为两个循环控制指令被融合在一起;这是一个好兆头。此外,在5.3B个时钟周期中的约1.3B个时钟周期(25%的时间)未发出任何uops,而在其余时间中几乎全部时间(4B个时钟周期),每次发出4个uops。
  • uops_executed_port.port_[0-7]:端口饱和。我们很健康。在16B个后融合的uops中,端口0、1和5分别在5.3B个周期内吃掉了5B个uops(这意味着它们被最优地分配:浮点、浮点、整数),端口6吃掉了1B个(融合的dec-branch操作),而端口2、3、4和7相比则吃掉了微不足道的数量。
  • resource_stalls:其中有13亿次发生,其中2/3是由于保留站(RS),另外三分之一是由于未知原因。
  • 从我们对uops_retired.allinst_retired.all进行比较构建的累积分布中,我们知道60%的时间正在退役4个uops,13%的时间没有uops,其余时间退役2个uops,否则数量微不足道。
  • (众多的*idq*计数):IDQ很少会阻止我们。
  • lsd:循环流检测器正在工作;将近16B个融合的uops从前端供应给它。
  • ild:指令长度解码不是瓶颈,也没有遇到任何长度更改前缀。
  • br_inst_exec/br_misp_exec:分支预测错误是一个微不足道的问题。
  • icache.misses:微不足道。
  • fp_assist:微不足道。未遇到非规格化数。(我相信如果没有DAZ非规格化数为零刷新,它们将需要协助,在这里应该注册)

在Intel Haswell上一切都很顺利。如果您能在您的机器上运行我的套件,那将非常好。

复制说明

  • 规则#1:在执行任何操作之前,请检查我的所有代码。永远不要盲目相信互联网上的陌生人。
  • 获取perfcountdemo.clibperfcount.clibperfcount.h,将它们放入同一个目录中并一起编译。
  • 获取perfcount.cMakefile,将它们放入同一个目录中,并make内核模块。
  • 使用GRUB启动标志nmi_watchdog=0 modprobe.blacklist=iTCO_wdt,iTCO_vendor_support重新启动您的机器。否则,NMI看门狗会干扰未停顿的核心周期计数器。
  • insmod perfcount.ko该模块。dmesg | tail -n 10应显示成功加载并显示3个Ff计数器和4个Gp计数器,否则给出原因。
  • 运行我的应用程序,最好在系统的其余部分不受负载的情况下运行。尝试通过更改pfcPinThread()的参数来限制您的关联所在的核心,以进行更改。
  • 在此处编辑结果。

1
看起来是一个很好的答案。我也只有Haswell可以测试。 - Z boson
1
@IwillnotexistIdonotexist:我不知道是否涉及到边缘计算,但是perf可以使用CMASK和UMASK。例如,ocperf的uops_dispatched_port.port_0计算了分派给该线程(而不是核心上任何线程)的端口0的微操作数。它运行perf stat -e cpu/event=0xa1,umask=0x1,name=uops_dispatched_port_port_0/ ./your_program。尽管ocperf是用Python编写的,但它只是在Python中进行参数解析和其他一些事情。最终它会运行perf,我认为它会在目标可执行文件的execve之前设置perf计数。因此,在目标程序运行时不会产生额外开销。 - Peter Cordes
1
@PeterCordes 我刚刚阅读了Haswell勘误文件。它让人感到恐惧 - 大部分PMC可能因各种原因而出现欠计数/过计数的情况。也许当计数本身存在噪声时,开销并不是那么重要!我仍然会尝试清理我的代码,使其更易于使用,也许可以使用perf的事件代号。 - Iwillnotexist Idonotexist
2
@Zboson 我已经更新了答案,列出了我的机器上更为相关的计数器结果,并将软件精简到可能更广泛有用的程度,例如像您这样的人。 - Iwillnotexist Idonotexist
1
@Zboson我真的不知道VirtualBox是否能够进行PMC直通。我的直觉是不行,但如果他们确实可以,那就很有趣了。此外,我的内核模块不需要任何人的许可就可以使用它们,并且只是写入配置MSR而不期望任何干扰,理论上这些计数器很少被任何东西使用。在我的系统上,当你禁用NMI看门狗时,这是真的;如果您的Windows机器也为此目的篡改它们,我不会尝试在通过PMC访问的VM上加载我的模块。 - Iwillnotexist Idonotexist
显示剩余10条评论

11

更新:之前版本包含了6个VPADDD指令(问题中为5个),而额外的VPADDD导致了Broadwell上的不平衡。在修复后,Haswell、Broadwell和Skylake向端口0、1和5发出的uops数量几乎相同。

没有端口污染,但uops被安排得不够优化,大多数uops都会堆积在Broadwell的端口5上,在端口0和1被饱和之前成为瓶颈。

为了说明情况,我建议(滥用)在PeachPy.IO网站上演示:

  1. 在Google Chrome浏览器中打开www.peachpy.io(其他浏览器无法使用)。

  2. 使用以下代码替换默认代码(该代码实现SDOT函数),这是你的示例转换为PeachPy语法的文字:

    n = Argument(size_t)
    x = Argument(ptr(const_float_))
    incx = Argument(size_t)
    y = Argument(ptr(const_float_))
    incy = Argument(size_t)
    
    with Function("sdot", (n, x, incx, y, incy)) as function:
        reg_n = GeneralPurposeRegister64()
        LOAD.ARGUMENT(reg_n, n)
    
        VZEROALL()
    
        with Loop() as loop:
            for i in range(15):
                ymm_i = YMMRegister(i)
                if i < 10:
                    VFMADD231PS(ymm_i, ymm_i, ymm_i)
                else:
                    VPADDD(ymm_i, ymm_i, ymm_i)
            DEC(reg_n)
            JNZ(loop.begin)
    
        RETURN()
    
  3. 我在PeachPy.io上有多台不同微架构的计算机作为后端。选择Intel Haswell、Intel Broadwell或Intel Skylake,然后按“快速运行”。系统将编译您的代码,上传到服务器,并可视化执行期间收集的性能计数器。

  4. 这是在Intel Haswell上执行端口上的uops分布:

  5. 在Intel Haswell上的端口压力

  6. 这是来自Intel Broadwell相同情况的图形:

  7. 在Intel Broadwell上的端口压力

  8. 显然,无论uops scheduler中存在什么缺陷,它都已在Intel Skylake中得到修复,因为该机器上的端口压力与Haswell相同。


另一方面,如果是18,则结果更有意义:似乎将5个FMA uop描绘到p0中,将5个FMA uop描绘到p1中,将1个dec + branch描绘到p6中,以及将5个VPADDD + 1个神秘的uop描绘到p5中。这些神秘的uops可能来自于没有dec + branch融合,或者来自于一个额外的VPADDD。 - Iwillnotexist Idonotexist
2
@Marat Dukhan 哈哈!你确实有6个VPADDDs。尝试将for i in range(16):替换为for i in range(15):。有趣的是HSW可以平衡这个额外的VPADDD,而BRW则不行。 - Iwillnotexist Idonotexist
2
@我将不会存在我不存在的确!没有注意到原始代码没有使用所有寄存器。 - Marat Dukhan
2
@MaratDukhan 我仍然认为你的帖子有意义;你清楚地展示了 Haswell 可以将 5 1/3 个 VPADDDs 转化为 p52/3 个 VPADDDs 转化为 p14 2/3 个 FMAs 转化为 p15 1/3 个 FMAs 转化为 p0,而 BRW 似乎无法做到。这值得进一步探讨。 - Iwillnotexist Idonotexist
3
@IwillnotexistIdonotexist @PeterCordes 你可以获取原始事件计数。使用PeachPy编译源文件,本地执行命令 python -m peachpy.x86_64 -mabi=sysv -mimage-format=elf -mcpu=haswell experiment.py -o experiment.o,然后使用 wget 上传,命令为 wget --header="Content-Type:application/octet-stream" --post-file=experiment.o "http://www.peachpy.io/run/broadwell?kernel=sdot&n=1000&incx=1&incy=1&offx=0&offy=0" -q -O -。如有需要,请将 broadwell 替换为 haswellskylake - Marat Dukhan
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接