Развертывание 1 цикла l oop снижает производительность на 25% на Skylake. вопрос планирования Uops? - PullRequest
5 голосов
/ 23 января 2020

TL; DR У меня есть все oop, для выполнения которого на Skylake требуется 1 цикл (3 добавления + 1 дюйм / прыжок).

Когда я разверну его больше, чем 2 раза (независимо от того, сколько) моя программа работает примерно на 25% медленнее. Возможно, это как-то связано с выравниванием, но я не совсем понимаю, что.

РЕДАКТИРОВАТЬ: этот вопрос раньше задавался вопросом о том, почему мопы были доставлены DSB, а не MITE. Теперь этот вопрос перенесен на этот вопрос .


Я пытался сравнить al oop, который делает 3 добавления на моем Skylake. Это l oop должно выполняться за один цикл, так как 3 add + 1 приращение сливаются с условным переходом, после того как слияние может выполняться за один цикл. И это, как и ожидалось.

Однако в какой-то момент мой компилятор C попытался развернуть этот l oop, что привело к ухудшению производительности. Сейчас я пытаюсь понять, почему развернутый l oop имеет худшую производительность, чем не развернутый, поскольку я ожидал, что оба будут иметь одинаковую производительность, или, может быть, развернутый будет медленнее, чем 15% .

Вот мой C код:

int main() {
  int a, b, c, d;

  #pragma unroll(2)
  for (unsigned long i = 0; i < 2000000000; i++) {
    asm volatile("" : "+r" (a), "+r" (b), "+r" (c), "+r" (d));
    a = a + d;
    b = b + d;
    c = c + d;
  }

  // Prevent data from being optimized out
  asm volatile("" : "+r" (a), "+r" (b), "+r" (c));

}

Компиляция с Clang 7.0.0 -O3 приводит к следующей (очищенной) сборке (с этого момента называемой v1) :

    movl    $2000000000, %esi
    .p2align    4, 0x90
.LBB0_1:
    addl    %edi, %edx
    addl    %edi, %ecx
    addl    %edi, %eax
    addl    %edi, %edx
    addl    %edi, %ecx
    addl    %edi, %eax
    addq    $-2, %rsi
    jne .LBB0_1

И сравнение с perf stat -e cycles показывает, что для каждой итерации требуется около 2 циклов.

Однако замена любого из регистров «новым 64-битным регистром» (r8 к r15) заставляет l oop выполняться в 3 циклах вместо 2 (давайте назовем этот код v2):

    movl    $2000000000, %esi
    .p2align    4, 0x90
.LBB0_1:
    addl    %edi, %r14d
    addl    %edi, %ecx
    addl    %edi, %eax
    addl    %edi, %r14d
    addl    %edi, %ecx
    addl    %edi, %eax
    addq    $-2, %rsi
    jne .LBB0_1

Это не случайный пример: Clang фактически производит это l oop если я добавлю кое-что в свою программу и получу неудачу (моя первоначальная версия была тем же C кодом, с дополнительной случайной инициализацией переменных, фазы разогрева и rdtscp для определения времени l oop и использования Clang r14d в л oop). Это l oop выполняется примерно за 3 цикла / итерация.

Дальнейшее тестирование показывает, что развертывание l oop любое число раз больше 2 заставляет программу выполняться за 2,5 миллиарда циклов (против 2 миллиардов для не развернутый).

Число мопов в al oop равно 3*n+1 (где n - коэффициент развертывания, а 1 - слитый add/jne), что означает, что l oop развернуто 3 раза по 10 мопов; 4 раза 13 мопсов и др. c. Это довольно небольшие количества мопов, которые должны помещаться в DSB (кеш мопов). Я на Skylake с обновленным микрокодом с исправлением для SKL150, так что мой LSD l oop буфер отключен .

Кроме того, развертывание 3, 4, 10 или 50 раз не вообще не меняйте производительность: мой код всегда выполняется за 2,5 миллиарда циклов (тогда как не развернутый код работает за 2 миллиарда циклов). Это несколько удивительно, поскольку 3 сложения должны всегда выполняться в 1 цикле, и, таким образом, если по какой-то причине в конце l oop был потерян дополнительный цикл, его издержки должны амортизироваться при увеличении развертки, а асимптотика * Производительность 1077 * (в коэффициенте развертывания) должна приблизиться к 2 млрд. Циклов.

Оба llvm-mca и iaca предсказывают, что при развертывании n раз выполнение l oop будет выполнено в n циклах (что приведет к выполнению всей программы за 2 миллиарда циклов).

Подводя итог, возникает вопрос: почему мои l oop 25% медленнее, как только Я разворачиваюсь более 2 раз?

...