При рассмотрении некоторых вопросов по оптимизации этот принятый ответ на вопрос о методах кодирования для наиболее эффективного использования оптимизатора вызвал у меня любопытство. Утверждение состоит в том, что для вычислений в функции следует использовать локальные переменные, а не выходные аргументы. Было высказано предположение, что это позволит компилятору выполнять дополнительные оптимизации, которые в противном случае были бы невозможны.
Итак, написание простого фрагмента кода для примера класса Foo и компиляция фрагментов кода с помощью g ++ v4.4 и -O2 дали некоторый вывод ассемблера (используйте -S). Части ассемблера, перечисленные только с частью цикла, показаны ниже. При рассмотрении вывода кажется, что цикл почти идентичен для обоих, с разницей только в одном адресе. Этот адрес является указателем на выходной аргумент для первого примера или на локальную переменную для второго.
Кажется, нет никаких изменений в фактическом эффекте, независимо от того, используется ли локальная переменная или нет.Таким образом, вопрос разбивается на 3 части:
a) GCC не выполняет дополнительную оптимизацию, даже с учетом предложенной подсказки;
b) GCC успешно оптимизирует оба случаев, но не должно быть;
c) GCC успешно оптимизирует в обоих случаях и производит совместимые выходные данные, как определено стандартом C ++?
Вот неоптимизированная функция:
void DoSomething(const Foo& foo1, const Foo* foo2, int numFoo, Foo& barOut)
{
for (int i=0; i
И соответствующая сборка:
.L3:
movl (%esi), %eax
addl $1, %ebx
addl $4, %esi
movl %eax, 8(%esp)
movl (%edi), %eax
movl %eax, 4(%esp)
movl 20(%ebp), %eax ; Note address is that of the output argument
movl %eax, (%esp)
call _ZN3Foo5mungeES_S_
cmpl %ebx, 16(%ebp)
jg .L3
Вот переписанная функция:
void DoSomethingFaster(const Foo& foo1, const Foo* foo2, int numFoo, Foo& barOut)
{
Foo barTemp = barOut;
for (int i=0; i
А вот вывод компилятора для функции, использующей локальную переменную:
.L3:
movl (%esi), %eax ; Load foo2[i] pointer into EAX
addl $1, %ebx ; increment i
addl $4, %esi ; increment foo2[i] (32-bit system, 8 on 64-bit systems)
movl %eax, 8(%esp) ; PUSH foo2[i] onto stack (careful! from EAX, not ESI)
movl (%edi), %eax ; Load foo1 pointer into EAX
movl %eax, 4(%esp) ; PUSH foo1
leal -28(%ebp), %eax ; Load barTemp pointer into EAX
movl %eax, (%esp) ; PUSH the this pointer for barTemp
call _ZN3Foo5mungeES_S_ ; munge()!
cmpl %ebx, 16(%ebp) ; i < numFoo
jg .L3 ; recall incrementing i by one coming into the loop
; so test if greater