Относительная производительность команды x86 inc vs. add

Быстрый вопрос, предполагая заранее

mov eax, 0

который более эффективен?

inc eax
inc eax

или

add eax, 2

Кроме того, в случае, если два inc работают быстрее, обычно ли компиляторы (скажем, GCC) (т.е. без агрессивных флагов оптимизации) оптимизируют var += 2?

Спасибо за ваше время!

PS: Не утруждайте себя ответом с изменением "не досрочно оптимизируйте", это просто академический интерес.

Ответы

Ответ 1

Две инструкции inc в том же регистре (или, вообще говоря, две инструкции чтения-изменения-записи) всегда имеют цепочку зависимостей, состоящую не менее чем из двух циклов. Это предполагает одну задержку в часах для inc, что имеет место с 486. Это означает, что если окружающие инструкции не могут чередоваться с двумя командами inc, чтобы скрыть эти задержки, код будет выполняться медленнее.

Но никакой компилятор не будет генерировать последовательность команд, которую вы предлагаете в любом случае (mov eax,0будет заменен на xor eax,eax, см. В чем цель XORing реестра с самим собой?)

mov eax,0
inc eax
inc eax

он будет оптимизирован для

mov eax,2

Ответ 2

Если вы когда-либо захотите узнать сырую статистику производительности инструкций x86, см. Dr Agner Fogs листинга (объем 4, если быть точным). Что касается части о компиляторах, которая зависит от генератора кода компилятора, а не о том, на что вы должны полагаться слишком много.

на стороне примечания: мне смешно/иронично, что в вопросе о производительности вы использовали MOV EAX,0 для нулевого регистра вместо XOR EAX,EAX: P (и если MOV EAX,0 было сделано заранее, самый быстрый вариант было бы удалить inc и добавить и просто MOV EAX,2).

Ответ 3

Для всех целей это, вероятно, не имеет значения. Но учтите, что inc использует меньше байтов.

Рассмотрим следующий код:

int x = 0;
x += 2;

Без использования каких-либо флагов оптимизации GCC компилирует этот код в:

80483ed:       c7 44 24 1c 00 00 00    movl   $0x0,0x1c(%esp)
80483f4:       00 
80483f5:       83 44 24 1c 02          addl   $0x2,0x1c(%esp)

Используя -O1 и -O2, он становится:

c7 44 24 08 02 00 00    movl   $0x2,0x8(%esp)

Смешно, не так ли?

Ответ 4

В руководстве Intel, которое вы можете найти здесь, похоже, что инструкции ADD/SUB в два раза дешевле одной конкретной архитектуры. Но помните, что Intel использует для него (недавние) процессоры модель исполнения вне очереди. Это в первую очередь означает, что узкие места производительности обнаруживаются там, где процессору приходится ждать ввода данных (например, в процессе получения данных L1/L2/L3/RAM не хватало вещей). Поэтому, если вы профилировщик говорит вам, что проблема с INC может быть проблемой; посмотрите на это, чтобы сформировать точку доступа к данным, вместо того, чтобы смотреть на количество необработанных циклов.

Instruction              Latency1           Throughput         Execution Unit 
                                                            2 
CPUID                    0F_3H    0F_2H      0F_3H    0F_2H    0F_2H 

ADD/SUB                  1        0.5        0.5      0.5      ALU 
[...]
DEC/INC                  1        1          0.5      0.5      ALU