Является ли a + = b более эффективным, чем a = a + b в C?

Я знаю на некоторых языках следующее:

a += b

более эффективен, чем:

a = a + b

поскольку он устраняет необходимость создания временной переменной. Это в C? Эффективно ли использовать + = (и, следовательно, также -= *= и т.д.)

Ответ 1

Итак, вот окончательный ответ...

$ cat junk1.c
#include <stdio.h>

int main()
{
    long a, s = 0;
    for (a = 0; a < 1000000000; a++)
    {
        s = s + a * a;
    }
    printf("Final sum: %ld\n", s);
}

[email protected]:~/junk$ cat junk2.c
#include <stdio.h>

int main()
{
    long a, s = 0;
    for (a = 0; a < 1000000000; a++)
    {
        s += a * a;
    }
    printf("Final sum: %ld\n", s);
}

[email protected]:~/junk$ for a in *.c ; do gcc -O3 -o ${a%.c} $a ; done
[email protected]:~/junk$ time ./junk1
Final sum: 3338615082255021824

real    0m2.188s
user    0m2.120s
sys 0m0.000s
[email protected]:~/junk$ time ./junk2
Final sum: 3338615082255021824

real    0m2.179s
user    0m2.120s
sys 0m0.000s

... для компьютера my и my, работающего в моей операционной системе. Ваши результаты могут варьироваться или не меняться. Однако в системе my время идентично: время пользователя 2.120s.

Теперь, чтобы показать вам, насколько впечатляющими могут быть современные компиляторы, вы заметите, что я использовал выражение a * a в задании. Это связано с этой небольшой проблемой:

$ cat junk.c
#include <stdio.h>

int main()
{
    long a, s = 0;
    for (a = 0; a < 1000000000; a++)
    {
        s = s + a;
    }
    printf("Final sum: %ld\n", s);
}

[email protected]:~/junk$ gcc -O3 -S junk.c
[email protected]:~/junk$ cat junk.s 
    .file   "junk.c"
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string "Final sum: %ld\n"
    .text
    .p2align 4,,15
.globl main
    .type   main, @function
main:
.LFB22:
    .cfi_startproc
    movabsq $499999999500000000, %rdx
    movl    $.LC0, %esi
    movl    $1, %edi
    xorl    %eax, %eax
    jmp __printf_chk
    .cfi_endproc
.LFE22:
    .size   main, .-main
    .ident  "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
    .section    .note.GNU-stack,"",@progbits

Компилятор вычислил мой цикл и развернул его до точки вычисления суммарной суммы и просто ввел ее как константу, которую он начал распечатывать, полностью пропуская любую конструкцию цикла. Перед лицом оптимизаторов, которые умны, вы действительно думаете, что собираетесь найти какой-либо значимый край в различии между s = s + a и s += a?!

Ответ 2

Это конкретный вопрос для компилятора, но я ожидаю, что все современные компиляторы дадут тот же результат. Использование Visual Studio 2008:

int main() {  
    int a = 10;  
    int b = 30;  
    a = a + b;  
    int c = 10;  
    int d = 50;  
    c += d;  
}  

Линия a = a + b имеет разборку

0014139C  mov         eax,dword ptr [a]   
0014139F  add         eax,dword ptr [b]   
001413A2  mov         dword ptr [a],eax   

Линия c + = d имеет разборку

001413B3  mov         eax,dword ptr [c]   
001413B6  add         eax,dword ptr [d]   
001413B9  mov         dword ptr [c],eax   

То же самое. Они скомпилированы в один и тот же код.

Ответ 3

Это зависит от того, что a.

a += b в C по определению эквивалентен a = a + b, за исключением того, что из абстрактной точки зрения a оценивается только один раз в первом варианте. Если a является "чистым" значением, т.е. Если оценивать его один раз и оценивать его много раз, это не влияет на поведение программ, тогда a += b строго эквивалентно a = a + b во всех отношениях, включая эффективность.

Другими словами, в ситуациях, когда у вас действительно есть свободный выбор между a += b и a = a + b (что означает, что вы знаете, что они делают то же самое), они, как правило, будут иметь одинаковую эффективность. Некоторые компиляторы могут испытывать трудности, когда a означает вызов функции (например, не то, что вы имели в виду), но когда a является энергонезависимой переменной, машинный код, сгенерированный для обоих выражений, будет таким же.

В другом примере, если a является изменчивой переменной, то a += b и a = a + b имеют разное поведение и, следовательно, различную эффективность. Однако, поскольку они не эквивалентны, ваш вопрос просто не применяется в таких случаях.

Ответ 4

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

s[i]->m[j1].k = s[i]->m[jl].k + 23;   // Was that a typo?

против

s[i]->m[j1].k += 23;

Два преимущества - и я не рассчитываю на меньшее количество ввода. Там нет вопроса о том, была ли опечатка, когда первое и второе выражения отличаются; и компилятор не оценивает сложное выражение дважды. Вероятно, в наши дни это не будет иметь большого значения (оптимизация компиляторов намного лучше, чем раньше), но у вас могут быть еще более сложные выражения (оценка функции, определенной в другой единицы перевода, например, как часть subscriptip), где компилятор не сможет избежать оценки выражения дважды:

s[i]->m[somefunc(j1)].k = s[i]->m[somefunc(j1)].k + 23;

s[i]->m[somefunc(j1)].k += 23;

Кроме того, вы можете написать (если вы храбры):

s[i++]->m[j1++].k += 23;

Но вы не можете написать:

s[i++]->m[j1++].k = s[i]->m[j1].k + 23;
s[i]->m[j1].k = s[i++]->m[j1++].k + 23;

(или любая другая перестановка), потому что порядок оценки не определен.

Ответ 5

a += b

более эффективен, чем

a = a + b

потому что первый принимает вам 6 нажатий клавиш, а последний выдает 9 нажатий клавиш.

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

Однако, как говорили другие, компилятор почти наверняка производит точно такой же код, чтобы первый был более эффективным.

Даже если вы учитываете читаемость, большинство программистов на C, вероятно, мысленно анализируют бывшее быстрее, чем последнее, потому что это такой общий шаблон.

Ответ 6

Практически во всех случаях они дают одинаковые результаты.

Ответ 7

Помимо оригинального или некомпетентно написанного компилятора, не должно быть разницы, если a и b являются нормальными переменными, поэтому они дают эквивалентные результаты.

Если вы имели дело с С++, а не с C, перегрузка оператора могла бы иметь более существенные отличия.