Может ли ЛСД выдавать uOP от следующей итерации обнаруженного цикла? - PullRequest
0 голосов
/ 28 августа 2018

Я играл, исследуя возможности филиала на порту 0 моего Haswell, начиная с очень простого цикла:

BITS 64
GLOBAL _start

SECTION .text

_start:

 mov ecx, 10000000

.loop:

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

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

.end:
 mov eax, 60
 xor edi, edi
 syscall

Используя perf, мы видим, что цикл работает на 1c / iter

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

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

Мои интерпретации этих результатов:

  • D и J отправляются параллельно.
  • J имеет обратную пропускную способность 1 цикл.
  • И D, и J отправляются оптимально.

Однако мы также видим, что RS никогда не заполняется.
Он может отправлять uOP с максимальной скоростью 2 uOP / c, но теоретически может получить 4 uOP / c, что приводит к полному RS примерно через 30 с (для RS с размером записей в 60 слитых доменов).

Насколько я понимаю, должно быть очень мало неправильных прогнозов ветвей, и все uOP должны исходить от ЛСД.
Поэтому я посмотрел на ИП:

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

   0.007429733 seconds time elapsed   ( +-  1.79% )

, который подтверждает, что FE выдает из LSD 1 .
Тем не менее, ЛСД никогда не выдает 4 uOPs / c:

     7,591,866      lsd_cycles_active ( +-  3.17% )
             0      lsd_cycles_4_uops 

Моя интерпретация заключается в том, что ЛСД не может выдавать uOPs из следующей итерации 2 , тем самым отправляя только пары D J в BE каждый цикл.
Правильна ли моя интерпретация?


Исходный код находится в этом хранилище .


1 Существует небольшая разница, я думаю, это связано с большим количеством итераций, которые допускают некоторое переключение контекста.
2 Звучит довольно сложно для аппаратных средств с ограниченной глубиной микросхемы.

1 Ответ

0 голосов
/ 29 августа 2018

Все мопы в вашем цикле являются ветвями (2 на итерацию). Я думаю, что причина того, что `lsd_cycles_4_uops равен нулю, заключается в ограничении переименователя. Согласно Руководству по оптимизации Intel, раздел 2.4.3.1:

Переименователь может выделять две ветви в каждом цикле по сравнению с одной ветвь каждого цикла в предыдущей микроархитектуре. Это может устранить некоторые пузыри в исполнении.

Это подраздел раздела микроархитектуры Песчаного моста. Но, насколько мне известно, это относится ко всем более поздним микроархитектурам. Максимальная пропускная способность переименования составляет 4 мопа за цикл. Но не более двух мопов могут быть ветвями. Таким образом, в этом примере, где все мопы являются ветвями, LSD никогда не сможет доставить более 2 мопов в любой заданный цикл даже на первой итерации цикла.

Таким образом, 2 RSU будут распределены в RS за цикл, и оба (один предикат принят и один не взят) могут быть отправлены за цикл. Так что заполняемость РС не растет.

Это ограничение не влияет на производительность вашей программы. Выполнение 2 переходов мопов за цикл, при котором IPC равен 3 за цикл, уже является оптимальным.

Я попытался найти событие производительности, которое может захватить задержки распределителя из-за этого ограничения. События RESOURCE_STALLS.ANY и UOPS_ISSUED.ANYcmask = 1 и inv = 1) в данном случае не актуальны. @IwillnotexistIdonotexist предложил использовать IDQ_UOPS_NOT_DELIVERED.CORE. Я представляю результаты ниже для события производительности и всех его поддерживаемых вариантов. Я также даю правильное значение этих событий, потому что руководство неверно. T обозначает количество итераций.

IDQ_UOPS_NOT_DELIVERED.CORE: подсчитывает количество слотов, которые не были использованы распределителем. Если программа выполнялась для циклов ядра C, то общее количество слотов равно 4 * C. Измеренное значение практически равно 2 * Т. Поскольку количество циклов равно T, количество временных интервалов равно 4 * T, что означает, что около половины временных интервалов выдачи не было использовано.

IDQ_UOPS_NOT_DELIVERED.CYCLES_0_UOPS_DELIV.CORE: Подсчитывает количество циклов, в течение которых с IDQ было доставлено ноль мопов. Измеренное значение пренебрежимо мало.

IDQ_UOPS_NOT_DELIVERED.CYCLES_LE_1_UOP_DELIV.CORE: подсчитывает количество циклов, в течение которых максимум 1 моп было доставлено из IDQ. Измеренное значение пренебрежимо мало.

IDQ_UOPS_NOT_DELIVERED.CYCLES_LE_2_UOP_DELIV.CORE: подсчитывает количество циклов, в течение которых с IDQ было доставлено не более 2 моп: измеренное значение почти равно T.

IDQ_UOPS_NOT_DELIVERED.CYCLES_LE_3_UOP_DELIV.CORE: подсчитывает количество циклов, в течение которых с IDQ было доставлено не более 3 моп: измеренное значение почти равно T.

Следовательно, поскольку время выполнения почти равно T базовых циклов, мы можем сделать вывод, что распределитель выделяет только 2 мопа за цикл в большинстве циклов, что равно скорости диспетчеризации.

Обратите внимание, что RS в Haswell и Skylake содержит неиспользованные мопы. Таким образом, каждая запись может содержать один неиспользованный моп. См. Сноска 2 . Но это не имеет значения, потому что здесь нет микрофузии.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...