英特尔Broadwell处理器中出现的重大FMA性能exception

  • 代码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 
  • 代码2:

     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 
  • Code3(与Code2相同,但是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 
  • Code7 :(与Code1相同,但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. 有人可以解释一下Broadwell上Code1的情况吗? 我的猜测是Broadwell在Code1的情况下用vpaddds污染了Port1,但是Haswell只有在Port0和Port1已满时才能使用Port5 ;

  2. 你有什么想法用FMA指令完成Broadwell的〜5000000秒钟吗?

  3. 我试图重新sorting。 类似的双重和qword经验;

  4. 我使用Windows 8.1和Win 10;

    更新:


  5. 增加Code3作为Marat Dukhan的想法与长的VEX;

  6. 用Skylake体验扩展结果表;

  7. 上传了VS2015社区+ MASM示例代码

    UPDATE2:


  8. 我尝试用xmm寄存器而不是ymm(代码4)。 同样的结果在Broadwell。

    UPDATE3:


  9. 我添加Code5作为彼得Cordes的想法(vpxdd与其他intructions(vpxor,vpor,vpand,vpandn,vpsubd))。 如果新的指令不是一个调零成语(vpxor,带有相同寄存器的vpsubd),那么在BDW上的结果是相同的。 使用Code4和Code5更新示例项目。

    UPDATE4:


  10. 我添加Code6作为Stephen Canon的想法(内存操作数)。 结果是〜820万克拉。 使用Code6更新示例项目;

  11. 我检查了CPU频率和AIDA64系统稳定性testing的可能性。 频率稳定,没有节stream的迹象;

    在这里输入图像说明

  12. 英特尔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 
  13. 我跟着jcomeau_ictx的想法,修改了Agner Fog的testp.zip(发布2015-12-22)BDW 306D4上的端口使用情况:

      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 

    在我看来,来自RS失速的Code1和Code2差异。 英特尔SDM备注:“由于没有可用的RS入口,因此周期停滞”。

    我怎样才能避免与FMA的这个摊位?

    Update5:


  14. Code6改变了,正如Peter Cordes引起了我的注意,只有vpaddds使用了内存操作数。 对HSW和SKL没有影响,BDW变差。

  15. 正如Marat Dukhan所测量的,不仅仅是vpadd / vpsub / vpand / vpandn / vpxor受到影响,还有其他Port5有界的指令,如vmovaps,vblendps,vpermps,vshufps,vbroadcastss;

  16. IwillnotexistIdonotexistbuild议,我尝试了其他操作数。 一个成功的修改是Code7,所有的vpaddds使用ymm15。 这个版本可以在BDWs上产生〜5000000个clks,但只是一段时间。 在约600万FMA对之后,它达到了平常的〜7730000克拉:

     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 
  17. 我尝试Code8的xmm版本作为Code8。 效果是相似的,但更快的运行时间持续更长时间。 我没有发现1.6GHz的i5-5250U和3.7GHz的i7-5775C之间的显着差异。

  18. 16和17是禁用超线程。 启用HTT后,效果会降低。

更新

我没有任何解释,因为我在哈斯韦尔,但我有代码分享可能会帮助您或其他人与Broadwell或Skylake硬件隔离您的问题。 如果您可以在您的机器上运行并分享结果,我们可以深入了解您的机器正在发生的事情。

介绍

最近的英特尔酷睿i7处理器有7个性能监控计数器(PMC),3个固定function和4个通用,可用于剖析代码。 固定function的PMC是:

  • 指令退休
  • Unhalted核心周期(包括TurboBoost效应的时钟周期)
  • Unharted参考周期(固定频率时钟滴答)

核心:参考时钟周期的比率决定了dynamic频率调整的相对加速或减速。

尽pipe存在访问这些计数器的软件(请参阅下面的注释),但我并不了解它们,但仍然发现它们不够精细。

因此,我在过去几天为自己写了一个Linux内核模块perfcount ,允许我访问英特尔性能计数器监视器,以及用于您的代码的用户空间testing平台和库,这些代码将您的FMA代码打包到我的LKM。 下面是关于如何重现我的设置的说明。

我的testing台源代码如下。 它变暖了,然后多次运行您的代码,通过一系列度量标准进行testing。 我把你的循环次数改成了十亿次 由于一次只能编程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

 > ./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左右,这是一个好兆头:这意味着我们可以很好地估计我们的开销。 同样适用于其他固定function计数器。 事实上,他们都匹配得相当好,意味着4批次的testing是从苹果到苹果的比较。
  • 在核心:参考周期约为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,这也是正确的:每个时钟周期2个FMA + 1个VPADDD,4个时钟周期,每5个时钟周期有2个额外的环路控制指令平行。
  • uops_issued.any :发出的指令数是uops_issued.any ,但是发出的uops_issued.any数是uops_issued.any 。 这是因为两个循环控制指令是融合在一起的。 好兆头。 此外,在5.3B(25%的时间周期)中大约有13B个时钟周期,没有发出uops,而剩下的时间(4B个时钟周期)几乎全部发出,一次发出4个uops。
  • uops_executed_port.port_[0-7] :端口饱和。 我们身体很好。 在16B融合后的微处理器中,端口0,1和5分别在5.3B周期(这意味着它们分布最佳:分别为Float,float,int),端口6bte1b(熔断的分支操作) ,港口2,3,4和7相比,可以忽略不计。
  • resource_stalls :发生了1.3B,其中2/3是由于保留站(RS)造成的,另外三分之一是由于不明原因造成的。
  • 从我们在uops_retired.allinst_retired.all上进行比较的累积分布inst_retired.all ,我们知道60%的时间是退休,0 13%是时间,剩下的时间是2小时,可以忽略不计除此以外。
  • (众多的*idq*计数):IDQ很less能*idq*我们。
  • lsd :循环stream检测器正在工作; 几乎有16B的电容器从前端提供给前端。
  • ild :指令长度解码不是瓶颈,也不会遇到一个长度可变的前缀。
  • br_inst_exec/br_misp_exec :分支预测br_inst_exec/br_misp_exec是一个微不足道的问题。
  • icache.misses :可以忽略不计。
  • fp_assist :可以忽略不计。 非常规没有遇到。 (我相信,如果没有DAZ非正规化零冲洗,他们需要一个帮助,这应该在这里注册)

所以在英特尔Haswell,这是平稳的航行。 如果你可以在你的机器上运行我的套件,那就太好了。

繁殖说明

  • 规则1:在做任何事情之前检查我的所有代码。 千万不要盲目信任互联网上的陌生人。
  • 抓住perfcountdemo.c , libperfcount.c和libperfcount.h ,把它们放在同一个目录下并将它们一起编译。
  • 抓住perfcount.c和Makefile ,把它们放在同一个目录下,然后make内核模块。
  • 用GRUB引导标志重启机器nmi_watchdog=0 modprobe.blacklist=iTCO_wdt,iTCO_vendor_support 。 否则,NMI看门狗将篡改非固定核心循环计数器。
  • insmod perfcount.ko模块。 dmesg | tail -n 10 dmesg | tail -n 10应该说成功装载,并且说有3个Ff计数器和4个Gp计数器,否则给出一个没有这样做的理由。
  • 运行我的应用程序,最好在系统其余部分没有负载的情况下运行。 尝试在perfcountdemo.c更改通过将参数更改为pfcPinThread()来限制亲和力的核心。
  • 在这里编辑结果。

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

没有港口污染,但是uops的计划并不理想,大部分的uops都要到Broadwell的5号港口,并且在港口0和1饱和之前成为瓶颈。

为了演示发生了什么,我build议(ab)使用PeachPy.IO上的演示:

  1. 在Google Chrome中打开www.peachpy.io(在其他浏览器中不起作用)。

  2. 用下面的代码replace默认代码(实现了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的后端,我有许多不同的微架构的机器。 selectIntel Haswell,Intel Broadwell或Intel Skylake,然后按“快速运行”。 系统将编译您的代码,将其上传到服务器,并可视化执行过程中收集的性能计数器。

  4. 以下是英特尔Haswell上执行端口上的uops分发:

英特尔Haswell的端口压力

  1. 这里是英特尔Broadwell的情节:

Intel Broadwell的端口压力

  1. 显然,无论是uops调度程序中的缺陷,它在Intel Skylake中都是固定的,因为该机器上的端口压力与Haswell相同。