Является ли a + = b более эффективным, чем a = a + b в C? - PullRequest
11 голосов
/ 17 февраля 2011

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

a += b

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

a = a + b

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

Ответы [ 7 ]

36 голосов
/ 17 февраля 2011

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

$ 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);
}

michael@isolde:~/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);
}

michael@isolde:~/junk$ for a in *.c ; do gcc -O3 -o ${a%.c} $a ; done
michael@isolde:~/junk$ time ./junk1
Final sum: 3338615082255021824

real    0m2.188s
user    0m2.120s
sys 0m0.000s
michael@isolde:~/junk$ time ./junk2
Final sum: 3338615082255021824

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

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

Теперь, просто чтобы показать вам, какими впечатляющими могут быть современные компиляторы, вы заметите, что я использовал выражение 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);
}

michael@isolde:~/junk$ gcc -O3 -S junk.c
michael@isolde:~/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?!

22 голосов
/ 17 февраля 2011

Это действительно вопрос, специфичный для компилятора, но я ожидаю, что все современные компиляторы дадут одинаковый результат. Использование 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   

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

17 голосов
/ 17 февраля 2011

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

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

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

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

6 голосов
/ 19 февраля 2011

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

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

против

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

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

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;

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

3 голосов
/ 17 февраля 2011
a += b

эффективнее

a = a + b

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

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

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

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

2 голосов
/ 17 февраля 2011

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

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

2 голосов
/ 17 февраля 2011

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

...