12

I know in some languages the following:

a += b

is more efficient than:

a = a + b

because it removes the need for creating a temporary variable. Is this the case in C? Is it more efficient to use += (and, therefore also -= *= etc)

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
robintw
  • 27,571
  • 51
  • 138
  • 205
  • 17
    Transforming one into the other is **nothing** compared to the manipulations modern compilers are able to do for optimization purposes. Write whichever you find more readable. – Pascal Cuoq Feb 17 '11 at 16:23
  • 1
    @Brian: C doesn't have reference types. – Johan Kotlinski Feb 17 '11 at 16:24
  • 1
    oh wow I totally misread the tag... – Brian Driscoll Feb 17 '11 at 16:24
  • I'd be curious to know in which languages there are performance differences and if you could produce a realistic example of a situation where it made a discernible difference. – David Heffernan Feb 17 '11 at 16:32
  • 1
    @Pascal: I agree - almost. I'd say, "write whichever is more readable for the majority of programmers". (Someone coming from BASIC might find `a=a+b` more readable, although it's different for the rest of the C++ world.) – sbi Feb 17 '11 at 16:34
  • @David: I've done quite a bit of programming in IDL, and I know that in IDL += can be a lot faster, particularly if you are doing operations with large arrays (IDL is an array-based language which is used in scientific computing). – robintw Feb 17 '11 at 16:46
  • 2
    @robintw That sounds like a flaw in IDL. Anytime two semantically equivalent instructions have different performance characteristics, that has to go down as a weakness in the language/interpreter/compiler/whatever. – David Heffernan Feb 17 '11 at 16:48
  • 3
    I have a silly question: why not ... test this? I mean seriously, how hard is it to code a loop that does a million of each and time each one? And the best part is you'll get a definitive answer instead of opinions of people using different compilers running on different operating systems and different processors from yours. – JUST MY correct OPINION Feb 17 '11 at 16:50
  • @JUST Timing such a thing is not easy. It's simplest to look at the emitted object code, which will be identical. – David Heffernan Feb 17 '11 at 16:59
  • @David This [blog post](http://michaelgalloy.com/2006/05/10/the-temporary-function.html) explains how it works in IDL - basically += is syntactic sugar which uses the TEMPORARY() function internally. This can lead to significant speedup. – robintw Feb 17 '11 at 17:00
  • @robintw Sounds like these IDL people don't really care about performance since inplace addition is pretty trivial – David Heffernan Feb 17 '11 at 17:04

7 Answers7

37

So here's a definitive answer...

$ 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

...for my computer and my compiler running on my operating system. Your results may or may not vary. On my system, however, the time is identical: user time 2.120s.

Now just to show you how impressive modern compilers can be, you'll note that I used the expression a * a in the assignment. This is because of this little problem:

$ 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

The compiler figured out my loop and unrolled it to the point of calculating the cumulative sum and just embedded that as a constant which it proceeded to print out, skipping any kind of looping construct entirely. In the face of optimizers that clever do you really think you're going to find any meaningful edge in distinguishing between s = s + a and s += a?!

JUST MY correct OPINION
  • 35,674
  • 17
  • 77
  • 99
22

This is a compiler specific question really, but I expect all modern compilers would give the same result. Using Visual Studio 2008:

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

The line a = a + b has disassembly

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

The line c += d has disassembly

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

Which is the same. They are compiled into the same code.

Jonathan Wood
  • 65,341
  • 71
  • 269
  • 466
17

It depends on what a is.

a += b in C is by definition equivalent to a = a + b, except that from the abstract point of view a is evaluated only once in the former variant. If a is a "pure" value, i.e. if evaluating it once vs. evaluating it many times makes no impact on programs behavior, then a += b is strictly equivalent to a = a + b in all regards, including efficiency.

In other words, in situations when you actually have a free choice between a += b and a = a + b (meaning that you know that they do the same thing) they will generally have exactly the same efficiency. Some compilers might have difficulties when a stands for a function call (for one example; probably not what you meant), but when a is a non-volatile variable the machine code generated for both expressions will be the same.

For another example, if a is a volatile variable, then a += b and a = a + b have different behavior and, therefore, different efficiency. However, since they are not equivalent, your question simply does not apply in such cases.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
6

In the simple cases shown in the question, there is no significant difference. Where the assignment operator scores is when you have an expression such as:

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

vs:

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

Two benefits - and I'm not counting less typing. There's no question about whether there was a typo when the first and second expressions differ; and the compiler doesn't evaluate the complex expression twice. The chances are that won't make much difference these days (optimizing compilers are a lot better than they used to be), but you could have still more complex expressions (evaluating a function defined in another translation unit, for example, as part of the subscripting) where the compiler may not be able to avoid evaluating the expression twice:

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

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

Also, you can write (if you're brave):

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

But you cannot write:

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

(or any other permutation) because the order of evaluation is not defined.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 1
    I wish I could give +10 for pointing out a situation where `+=` makes things more readable and reliable without bringing up the "fewer keystrokes" canard. (Not to mention bringing up the kinds of situation where the semantic difference between `+=` and the `=...+` makes a difference.) – JUST MY correct OPINION Feb 20 '11 at 07:19
3
a += b

is more efficient than

a = a + b

because the former takes you 6 keystrokes and the latter takes you 9 keystrokes.

With modern hardware, even if the compiler is stupid and uses slower code for one than the other, the total time saved over the lifetime of the program may possibly be less than the time it takes you to type the three extra key strokes.

However, as others have said, the compiler almost certainly produces exactly the same code so the former is more efficient.

Even if you factor in readability, most C programmers probably mentally parse the former more quickly than the latter because it is such a common pattern.

JeremyP
  • 84,577
  • 15
  • 123
  • 161
  • 1
    If you are measuring efficiency in keystrokes you are programming VERY badly. – JUST MY correct OPINION Feb 20 '11 at 07:17
  • @JUST MY correct OPINION: When you program, you are trying to solve a problem. The time spent writing the program is a contribution to the overall cost of producing the solution, just as the time spent running the finished program is. Do not make the mistake of assuming your own time (as the programmer) has no value. – JeremyP Feb 28 '11 at 09:41
  • If three keystrokes makes that much of a difference to your trying to solve a problem, you are programming VERY badly. – JUST MY correct OPINION Mar 01 '11 at 04:28
  • 2
    @JUST MY correct OPINION: What do you mean by "that much of a difference"? Where did I quantify the saving? How does the fact that it takes longer to press nine keys in sequence than six keys in sequence mean that your programming is very bad? – JeremyP Mar 01 '11 at 09:07
2

In virtually all cases, the two produce identical results.

Jonathan Wood
  • 65,341
  • 71
  • 269
  • 466
  • Now I'm curious, care to bring up a scenario where they wouldn't be identical? – Brad Christie Feb 17 '11 at 16:29
  • @Brad I recall reading about how more compact C constructs *tended* to produce more compact code. I can't point to a specific case but I was thinking in terms of very early C compilers. I can't see any modern compiler producing different results. – Jonathan Wood Feb 17 '11 at 16:33
  • Fair enough, you just you peaked my curiosity with the use of virtually. Thanks for the answer. – Brad Christie Feb 17 '11 at 16:38
2

Other than with a truly ancient or incompetently written compiler there should be no difference, as long as a and b are normal variables so the two produce equivalent results.

If you were dealing with C++ rather than C, operator overloading would allow there to be more substantial differences though.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111