почему для цикла есть 1 дополнительная инструкция, чем ожидалось?

Я пишу много векторизованных циклов, поэтому 1 общая идиома

volatile int dummy[1<<10];
for (int64_t i = 0; i + 16 <= argc; i+= 16)   // process all elements with whole vector
{
  int x = dummy[i];
}
// handle remainder (hopefully with SIMD too)

но полученный машинный код имеет на 1 больше инструкций ,чем я хотел бы (используя gcc 4.9)

.L3:
        leaq    -16(%rax), %rdx
        addq    , %rax
        cmpq    %rcx, %rax
        movl    -120(%rsp,%rdx,4), %edx
        jbe     .L3

если я изменю код на for (int64_t i = 0; i <= argc - 16; i+= 16), тогда "лишние" инструкция пропала:

.L2:
        movl    -120(%rsp,%rax,4), %ecx
        addq    , %rax
        cmpq    %rdx, %rax
        jbe     .L2

но почему разница? Я думал, может быть, это из-за инвариантов цикла, но слишком смутно. Затем я заметил, что в случае с инструкцией 5 инкремент выполняется перед нагрузкой, которая будет требуется дополнительный ход из-за разрушительных инструкций 2 операнда x86. Таким образом, другое объяснение может заключаться в том, что это параллелизм торговых инструкций для 1 дополнительной инструкции.

хотя кажется, что вряд ли будет разница в производительности, может ли кто-нибудь объяснить эту тайну (желательно, кто знает о преобразованиях компилятора)?

В идеале я хотел бы сохранить форму I + 16

1 ответов


если argc были ниже -2147483632, и i был ниже 2147483632, выражения i+16 <= argc потребуется, чтобы получить арифметически правильный результат, в то время как выражение и i<argc-16 не будет. Необходимость дать арифметически правильный результат в этом угловом случае не позволяет компилятору оптимизировать первое выражение для соответствия последнему.