Значительная аномалия производительности FMA в процессоре Intel Broadwell

  • Code1:

    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
    
  • Код3 (такой же, как 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
    
  • Код4 (то же, что и 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
    
  • Код5 (такой же, как Code1, но с ненулевым vpsubd`s):

    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
    

Измеренные часы TSC с отключенным Turbo и C1E:

          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 
  • Может кто-нибудь объяснить, что происходит с Code1 на Бродвелл? Мое предположение Broadwell каким-то образом загрязняет Port1 с vpaddds в случае Code1, однако Haswell может использовать Port5 только в том случае, если Port0 и Port1 заполнены;

  • Есть ли у вас идея совершить ~ 5000000 clk на Broadwell с инструкциями FMA?

  • Я попытался изменить порядок. Аналогичное поведение наблюдается при использовании double и qword;

  • Я использовал Windows 8.1 и Win 10;

    Обновление:


  • Добавлен Code3 как идея Марата Духана с длинным VEX;

  • Расширенная таблица результатов с опытом Skylake;

  • Загрузили пример VS2015 Community + MASM здесь

    Update2:


  • Я попробовал с xmm-регистрами вместо ymm (код 4). Тот же результат на Бродвелле.

    Update3:


  • Я добавил Code5 как идею Питера Кордеса (замените vpaddd `s другими конструкциями (vpxor, vpor, vpand, vpandn, vpsubd)). Если новая инструкция не является идиомой обнуления (vpxor, vpsubd с тем же регистром), результат будет таким же на BDW. Пример проекта обновлен с помощью Code4 и Code5.

    Update4:


  • Я добавил Code6 в качестве идеи Стивена Канона (операнды памяти). Результат - ~ 8200000 clks. Пример проекта обновлен с помощью Code6;

  • Я проверил частоту процессора и возможное трение с помощью теста стабильности системы AIDA64. Частота стабильна и не имеет признаков дросселирования;

    введите описание изображения здесь

  • 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 и модифицировал testp.zip Agner Fog (опубликовано в формате 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
    

    Распределение портов близко к совершенству, как на Хасуэлле. Затем я проверил счетчики ресурсов (событие 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?

    Update5:


  • Code6 изменился, так как Питер Кордес привлек мое внимание, только vpaddds используют операнды памяти. Влияние на HSW и SKL, BDW ухудшается.

  • Как оценил Марат Духан, а не только vpadd/vpsub/vpand/vpandn/vpxor, но другие ограниченные команды Port5, такие как vmovaps, vblendps, vpermps, vshufps, vbroadcastss;

  • Как предложил IwillnotexistIdonotexist, я опробовал другие операнды. Успешной модификацией является Code7, где все vpaddds используют ymm15. Эта версия может работать на BDWs ~ 5000000 clks, но только на время. После ~ 6 миллионов пар FMA он достигает обычного ~ 7730000 clks:

    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
    
  • Я попробовал xmm версию Code7 как Code8. Эффект подобен, но более быстрое время работы сохраняется дольше. Я не нашел существенной разницы между 1,6 ГГц i5-5250U и 3,7 ГГц i7-5775C.

  • 16 и 17 были сделаны с отключенным HyperThreading. С включенным HTT эффект меньше.

Ответ 1

Обновление

У меня нет объяснений для вас, так как я нахожусь на Хасуэлле, но у меня есть код для обмена, который может помочь вам или кому-то еще с оборудованием Broadwell или Skylake изолировать вашу проблему. Если бы вы могли запустить его на своем компьютере и поделиться результатами, мы могли бы получить представление о том, что происходит с вашей машиной.

Введение

В новейших процессорах Intel Core i7 есть 7 счетчиков контроля производительности (PMC), 3 фиксированной функции и 4 универсальных устройства, которые могут использоваться для кода профиля. PMC с фиксированной функцией:

  • Инструкции вышли на пенсию
  • Неактивные циклы ядра (тики часов, включая эффекты TurboBoost)
  • Необработанные опорные циклы (галочки с фиксированной частотой)

Отношение сердечников: опорные тактовые циклы определяют относительное ускорение или замедление от масштабирования динамической частоты.

Хотя программное обеспечение существует (см. комментарии ниже), которое обращается к этим счетчикам, я не знал их и все еще считаю их недостаточно мелкозернистыми.

В течение последних нескольких дней я написал себе модуль ядра Linux perfcount, чтобы предоставить мне доступ к мониторам производительности Intel, а также тестовый стенд и библиотеку для вашего кода, который обертывает ваш код FMA вокруг звонков на мой LKM. Появятся инструкции по воспроизведению моей установки.

Мой исходный код testbench приведен ниже. Он прогревается, а затем запускает ваш код несколько раз, проверяя его на длинный список показателей. Я изменил количество циклов до 1 миллиарда. Поскольку сразу можно запрограммировать только 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

Мы можем видеть, что на Хасуэле все хорошо смазано. Я сделаю несколько заметок из приведенных выше характеристик:

  • Выданные инструкции невероятно последовательны для меня. Это всегда вокруг 17000001800, что является хорошим знаком: это означает, что мы можем очень хорошо оценить наши накладные расходы. То же самое для других счетчиков фиксированной функции. Тот факт, что все они соответствуют достаточно хорошо, означает, что тесты в партиях по 4 являются сравнениями яблок и яблок.
  • При коэффициенте ядра: эталонных циклах около 5305920785/4245764952 мы получаем среднее частотное масштабирование ~ 1,25; Это хорошо объясняет мои наблюдения, что мое ядро ​​увеличилось с 2,4 ГГц до 3,0 ГГц. cpu_clk_unhalted.core_clk/(10.0*cpu_clk_unhalted.ref_xclk) также дает менее 3 ГГц.
  • Соотношение инструкций, выданных для основных циклов, дает IPC, 17000001807/5305920785 ~ 3.20, что также касается права: 2 FMA + 1 VPADDD каждый тактовый цикл для 4 тактов и 2 дополнительных инструкций управления циклом каждые 5 тактов которые идут параллельно.
  • uops_issued.any: Количество выданных инструкций составляет ~ 17B, но количество выпущенных uops составляет ~ 16B. Это потому, что две команды для управления контуром соединяются друг с другом; Хороший знак. Кроме того, около 1,3 Б тактовых циклов из 5.3B (25% времени), не было выпущено ни одного uops, в то время как почти полная оставшаяся часть времени (тактовые циклы 4B), выдается 4 uops за раз.
  • uops_executed_port.port_[0-7]: насыщенность порта. У нас хорошее здоровье. Из 16-бит пост-фьюжн-портов, порты 0, 1 и 5 потребляли 5 Б, каждый из которых за 5,3 Б циклов (что означает, что они были распределены оптимально: Float, float, int соответственно), порт 6 ate 1B (скомпилированный dec-branch op), а порты 2, 3, 4 и 7 сравнивались с незначительными количествами.
  • resource_stalls: из них произошло 1.3B, 2/3 из которых были связаны с станцией резервирования (RS), а другая треть - с неизвестными причинами.
  • Из кумулятивного распределения, которое мы построили с нашими сравнениями на uops_retired.all и inst_retired.all, мы знаем, что мы удаляем 4 раза в 60% случаев, 0 - 13% времени и 2 раза в остальное время, с незначительными суммами в противном случае.
  • (Множество *idq* counts): IDQ редко вызывает нас.
  • lsd: работает Loop Stream Detector; С его интерфейсом были подключены почти 16-вольтовые предохранители.
  • ild: Декодирование длины команды не является узким местом, и не встречается ни одного префикса с изменением длины.
  • br_inst_exec/br_misp_exec: неверное предсказание отрасли является незначительной проблемой.
  • icache.misses: Незначительный.
  • fp_assist: Незначительный. Денормалы не встречаются. (Я считаю, что без денормалей DAZ - сброс нуля, им потребуется помощь, которая должна зарегистрироваться здесь).

Итак, на Intel Haswell это плавное плавание. Если бы вы могли запускать мой пакет на своих машинах, это было бы здорово.

Инструкции по воспроизведению

  • Правило №1: Осмотрите весь мой код, прежде чем что-либо делать с ним. Никогда не слепо доверять незнакомцам в Интернете.
  • Grab perfcountdemo.c, libperfcount.c и libperfcount.h, поместите их в один каталог и скомпилируйте их вместе.
  • Grab perfcount.c и Makefile, поместите их в один каталог и make модуль ядра.
  • Перезагрузите компьютер с помощью загрузочных флагов GRUB nmi_watchdog=0 modprobe.blacklist=iTCO_wdt,iTCO_vendor_support. Наблюдатель NMI будет вмешиваться в счетчик с неактивным сердечником в противном случае.
  • insmod perfcount.ko модуль. dmesg | tail -n 10 должен сказать, что он успешно загружен и говорит, что есть 3 счетчика Ff и счетчики 4 Gp, или же причина для отказа от этого.
  • Запустите мое приложение, желательно, пока остальная часть системы не находится под нагрузкой. Попробуйте также изменить в perfcountdemo.c ядро, к которому вы ограничиваете свое сродство, изменив аргумент на pfcPinThread().
  • Отредактируйте здесь результаты.

Ответ 2

Обновление: предыдущая версия содержала инструкции 6 VPADDD (vs 5 в вопросе), а дополнительный VPADDD вызвал дисбаланс в Broadwell. После того, как он был исправлен, Хасуэлл, Бродвелл и Скайлак выпустили почти такое же количество оборотов к портам 0, 1 и 5.

Отсутствует загрязнение портов, но uops запланированы субоптимально, причем большинство портов отправляется в порт 5 на Broadwell и делает это узким местом до того, как порты 0 и 1 будут насыщены.

Чтобы продемонстрировать, что происходит, я предлагаю (ab) использовать демо на PeachPy.IO:

  • Откройте www.peachpy.io в Google Chrome (он не будет работать в других браузерах).

  • Замените код по умолчанию (который реализует функцию 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()
    
  • У меня есть несколько машин на разных микроархитектурах в качестве бэкэнда для PeachPy.io. Выберите Intel Haswell, Intel Broadwell или Intel Skylake и нажмите "Быстрый запуск". Система будет компилировать ваш код, загрузить его на сервер и визуализировать счетчики производительности, собранные во время выполнения.

  • Вот дистрибутив uops через порты выполнения на Intel Haswell:

Давление порта на Intel Haswell

  1. И вот тот же сюжет от Intel Broadwell:

Напряжение порта на Intel Broadwell

  1. По-видимому, каков бы ни был недостаток в планировщике uops, он был исправлен в Intel Skylake, потому что давление порта на эту машину такое же, как у Haswell.