9

I realize the difference may be negligible, but which is more efficient in trying to zero an unsigned long?

unsigned long x;

...

x=0;

--OR--

x^=x;

Taylor

Mysticial
  • 464,885
  • 45
  • 335
  • 332
tejloro
  • 125
  • 6

3 Answers3

33

If you were to implement a compiler, what would you do? Indeed, you would pick the fastest implementation for both. Since both are equal, this fastest implementation is the same for both.

In other words, any compiler released after 5000 B.C. will generate the same assembly code for both x = 0 and x ^= x if you enable optimizations. This means that they are equally fast.

This doesn't go for only assignment/xorring, but also for multiplication, among other algorithms. Express your intent and let the compiler optimize it. The compiler is better at optimizations than you are, trust me.

In other words, write readable code and use x = 0;.


Oh and by the way, bitwise xorring an uninitialized integer by itself is undefined behavior and a good compiler should optimize out the entire thing.

Community
  • 1
  • 1
11

First of all, if the variable has not been assigned a value, it is technically "undefined behaviour" to do anything other than assign a value to it.

Second, to XOR it with itself is unlikely to be faster on a processor produced in the last 15-20 years, since it requires an extra read. It may have been faster (due to being SHORTER CODE) a very long time back, but actually, I believe even that is false.

Edit: I should point out that it MAY still be faster/more compact to XOR a register to make it zero in modern processors. But if we assume that we can't know if x is in a register or not, then we should also not make it more complicated for the compiler to determine what we're actually doing.

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
  • 4
    Actually, xoring a register with itself is the standard and fastest way to zero it. Modern hardware takes it as a special case to avoid the read dependency. It is most often resolved via register renaming with a pool of zero registers. – Mysticial Apr 04 '13 at 16:44
  • As you were writing that, I was editing my answer... – Mats Petersson Apr 04 '13 at 16:46
  • @Mysticial: Note that the CPU is fairly smart about it too -- for example, it doesn't just know about `xor rax, rax`, but also `sub rax,rax` so the two end up executing identically. – Jerry Coffin Apr 04 '13 at 16:47
  • @JerryCoffin The `sub reg,reg` recognition only started appearing in the Intel Sandy Bridge line. AFAIK, it didn't exist before. So it's relatively new. – Mysticial Apr 04 '13 at 16:48
  • @Mysticial: It originally appeared much earlier than that, then somehow got lost for a while, and finally get found again, so to speak. – Jerry Coffin Apr 04 '13 at 17:02
  • @JerryCoffin That I didn't know... :) – Mysticial Apr 04 '13 at 17:02
  • 3
    +1 for the first paragraph. The most important point here is that code which is attempting to set an uninitialized variable to 0 via the "xor trick" has undefined behavior. A compiler is perfectly free to treat such an operation as a promise that the statement is unreachable, and propagate the unreachable status to any other code which unconditionally leads to the statement. – R.. GitHub STOP HELPING ICE Apr 04 '13 at 17:52
  • An uninitialized object has indeterminate value, according to C++11, section 8.5. So this trick will work. – Derek Ledbetter Apr 04 '13 at 18:10
2

Why speculate about what the compiler does? Let's try it out instead!

Here's some test code:

void fzero()
{
   unsigned long x;

   x = 0;
}

void fxor()
{
   unsigned long x;

   x ^= x;
}


int main()
{
   fzero();
   fxor();
}

And now lets look at the resulting machine code:

; 10   :    unsigned long x;
; 11   : 
; 12   :    x ^= x;
; 13   : }

00000   c2 00 00     ret     0

; 3    :    unsigned long x;
; 4    : 
; 5    :    x = 0;
; 6    : }

00000   c2 00 00     ret     0

PUBLIC  main
; Function compile flags: /Ogtpy
;   COMDAT main
_TEXT   SEGMENT
main    PROC                        ; COMDAT

; 18   :    fzero();
; 19   :    fxor();
; 20   : }

  00000 33 c0        xor     eax, eax
  00002 c3       ret     0
main    ENDP

Oh, Look! They were equally fast, both taking exactly 0 ns.

Bo Persson
  • 90,663
  • 31
  • 146
  • 203