Почему `mov% eax,% eax;Nop` быстрее, чем `NOP`? - PullRequest
0 голосов
/ 25 октября 2018

Очевидно, , современные процессоры могут определить, делаете ли вы что-то глупое, например, перенести регистр в себя (mov %eax, %eax), и оптимизировать его.Пытаясь проверить это утверждение, я запустил следующую программу:

#include <stdio.h>
#include <time.h>

static inline void f1() {
   for (int i = 0; i < 100000000; i++)
      __asm__(
            "mov %eax, %eax;"
            "nop;"
            );
}

static inline void f2() {
   for (int i = 0; i < 100000000; i++)
      __asm__(
            "nop;"
            );
}

static inline void f3() {
   for (int i = 0; i < 100000000; i++)
      __asm__(
            "mov %ebx, %eax;"
            "nop;"
            );
}

int main() {
   int NRUNS = 10;
   clock_t t, t1, t2, t3;

   t1 = t2 = t3 = 0;
   for (int run = 0; run < NRUNS; run++) {
      t = clock(); f1(); t1 += clock()-t;
      t = clock(); f2(); t2 += clock()-t;
      t = clock(); f3(); t3 += clock()-t;
   }

   printf("f1() took %f cycles on avg\n", (float) t1/ (float) NRUNS);
   printf("f2() took %f cycles on avg\n", (float) t2/ (float) NRUNS);
   printf("f3() took %f cycles on avg\n", (float) t3/ (float) NRUNS);

   return 0;
}

Это дает мне:

f1() took 175587.093750 cycles on avg
f2() took 188313.906250 cycles on avg
f3() took 194654.296875 cycles on avg

Как и ожидалось, f3() выходит медленнее.Но удивительно (по крайней мере для меня), f1() быстрее, чем f2().Почему это так?

Обновление : Компиляция с -falign-loops дает качественно такой же результат:

f1() took 164271.000000 cycles on avg
f2() took 173783.296875 cycles on avg
f3() took 177765.203125 cycles on avg

1 Ответ

0 голосов
/ 26 октября 2018

Часть связанной статьи, которая заставила меня думать, что это можно оптимизировать, это: «функция перемещения заботится о проверке эквивалентных местоположений»

Это говорит о (move r x) функция в SBCL, а не инструкция x86 mov .Речь идет об оптимизации во время генерации кода из этого низкоуровневого промежуточного языка, а не во время выполнения аппаратными средствами.

Ни mov %eax, %eax, ни nop не являются полностью бесплатными.Они оба требуют пропускной способности внешнего интерфейса, и mov %eax,%eax даже не является NOP в 64-битном режиме (он ноль расширяет EAX до RAX, и потому что это тот же регистр, что исключение mov на процессорах Intel не удается).

См. Может ли MOV x86 действительно быть "свободным"?Почему я вообще не могу воспроизвести это? , чтобы больше узнать о узких местах пропускной способности внешнего / внутреннего интерфейса и задержке.


Возможно, вы видите некоторый побочный эффект кодавыравнивание, или, может быть, эффектный эффект задержки пересылки в хранилище семейства Sandybridge, как в Добавление избыточного назначения ускоряет код при компиляции без оптимизации , потому что вы также компилировали с отключенной оптимизацией, заставляя ваш компилятор создавать антиоптимизированный коддля последовательной отладки, которая сохраняет счетчик цикла в памяти.(~ 6 циклов переносимых в цикле зависимостей через сохранение / перезагрузку вместо 1 итерации за такт для обычного крошечного цикла.)

Если ваши результаты воспроизводимы с большим числом итераций, вероятно, есть какое-то микроархитектурное объяснение того, чтовы видите, но, вероятно, это не связано с тем, что вы пытались измерить.

Конечно, вам также нужно исправить ошибку mov %ebx, %eax; в f3, чтобы успешно компилировать с включенной оптимизацией.Закупорка EAX без указания компилятора наступит на сгенерированный компилятором код.Вы не объяснили, что вы пытались проверить с этим, так что IDK, если это была опечатка.

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