Live data from Hacker News

The Three Ways of XOR

horia141.com

61–69 of 69 posts

Re: The Three Ways of XOR

#61

Another way to look at XOR - it's an adder (the sum, without the carry)

The post states at the beginning of the 6th paragraph:

"Some extra treats: XOR can be considered an imparity function. When the number of inputs is even, it outputs zero, while when it is odd, it outputs one. It is also the sum part of an adder, that is, without the carry part. "

Re: The Three Ways of XOR

#62
post #56

Earlier quoted context omitted.

15 + 15 gives 14 14 - 15 gives 15

The way I wrote this was rubbish, edited. I was trying to get at if you have 1110, 1111 then 1110 + 1111 will overflow, but you could xor them.

  1110 + 1111 = 1101
  1101 - 1110 = 1111
  1101 - 1111 = 1110
Overflow doesn't matter if you just want to swap.

Re: The Three Ways of XOR

#63
post #26
post #2

> most programming languages don’t have an explicit “logical operator” for it I guess the author never taught of != . The only thing to be careful with is that it doesn't implicitly convert arguments to boolean, so expressions like " != (flags & Flag)" will go wrong without an explicit conversion "bool(flags & Flag)" or equivalent expression like "((flags & Flag) != 0)". And let's not forget about the friend, ==. I'v…

If you only want to convert the second argument you can use the ==! operator ;)

C has so many wonderful operators, why don't people use them?

  while (x --> 0) x goes to zero;
  while (0 

Re: The Three Ways of XOR

#64
post #62

Earlier quoted context omitted.

The way I wrote this was rubbish, edited. I was trying to get at if you have 1110, 1111 then 1110 + 1111 will overflow, but you could xor them.

1110 + 1111 = 1101 1101 - 1110 = 1111 1101 - 1111 = 1110 Overflow doesn't matter if you just want to swap.

(Assuming two's complement arithmetic, as amelius pointed out - I don't know if all programming languages and platforms use it)

Re: The Three Ways of XOR

#65
post #48
post #44

Earlier quoted context omitted.

Yes, in fact compilers these days are smart enough to convert people's xor swaps into mov swaps: https://godbolt.org/g/FYv7xQ

When I look at the generated code that multiple of the compilers (gcc,clang,icc) generate mov eax, edi mov edi, esi mov esi, eax I would intuitively use the `xchg` instruction that x86-32/x86-64 provides instead. Is there a specific reason why the compiler(s) decide to generate the mentioned code instead?

When used to swap with data in memory, the reason is that it is faster. xchg is atomic. To do that, it implicitly locks its target address. That makes it slower than the series of moves. http://www.agner.org/optimize/instruction_tables.pdf:

"Instructions with a LOCK prefix have a long latency that depends on cache organization and possibly RAM speed. If there are multiple processors or cores or direct memory access (DMA) devices then all locked instructions will lock a cache line for exclusive access, which may involve RAM access. A LOCK prefix typically costs more than a hundred clock cycles, even on single-processor systems. This also applies to the XCHG instruction with a memory operand."

Re: The Three Ways of XOR

#66
post #65
post #48

Earlier quoted context omitted.

When I look at the generated code that multiple of the compilers (gcc,clang,icc) generate mov eax, edi mov edi, esi mov esi, eax I would intuitively use the `xchg` instruction that x86-32/x86-64 provides instead. Is there a specific reason why the compiler(s) decide to generate the mentioned code instead?

When used to swap with data in memory, the reason is that it is faster. xchg is atomic. To do that, it implicitly locks its target address. That makes it slower than the series of moves. http://www.agner.org/optimize/instruction_tables.pdf : "Instructions with a LOCK prefix have a long latency that depends on cache organization and possibly RAM speed. If there are multiple processors or cores or direct memory access…

I know that. But this is only relevant if you exchange registers with memory and is not of relevance if you exchange two registers. I accept that this is a good point if some variables are moved to the stack because of register spilling or because you want to use the address of the variable (which is not the case here).

So I still stand by my point: What is the reason why the compiler uses `mov` for exchanging two registers here instead of `xchg`?

Re: The Three Ways of XOR

#67
post #66
post #65

Earlier quoted context omitted.

When used to swap with data in memory, the reason is that it is faster. xchg is atomic. To do that, it implicitly locks its target address. That makes it slower than the series of moves. http://www.agner.org/optimize/instruction_tables.pdf : "Instructions with a LOCK prefix have a long latency that depends on cache organization and possibly RAM speed. If there are multiple processors or cores or direct memory access…

I know that. But this is only relevant if you exchange registers with memory and is not of relevance if you exchange two registers. I accept that this is a good point if some variables are moved to the stack because of register spilling or because you want to use the address of the variable (which is not the case here). So I still stand by my point: What is the reason why the compiler uses `mov` for exchanging two re…

I think that's because (at least on some CPUs) it takes three macro-operations. http://www.agner.org/optimize/microarchitecture.pdf (section 17.4, page 188):

"Vector path instructions are less efficient than single or double instructions because they require exclusive access to the decoders and pipelines and do not always reorder optimally. For example:

    ; Example 17.1. AMD instruction breakdown
    xchg  eax, ebx   ; Vector path, 3 ops
    nop              ; Direct path, 1 op
    xchg  ecx, edx   ; Vector path, 3 ops
    nop              ; Direct path, 1 op
This sequence takes 4 clock cycles to decode because the vector path instructions must decode alone."

Re: The Three Ways of XOR

#68
post #67
post #66

Earlier quoted context omitted.

I know that. But this is only relevant if you exchange registers with memory and is not of relevance if you exchange two registers. I accept that this is a good point if some variables are moved to the stack because of register spilling or because you want to use the address of the variable (which is not the case here). So I still stand by my point: What is the reason why the compiler uses `mov` for exchanging two re…

I think that's because (at least on some CPUs) it takes three macro-operations. http://www.agner.org/optimize/microarchitecture.pdf (section 17.4, page 188): "Vector path instructions are less efficient than single or double instructions because they require exclusive access to the decoders and pipelines and do not always reorder optimally. For example: ; Example 17.1. AMD instruction breakdown xchg eax, ebx ; Vector…

This is indeed a good explanation - I admit I was not aware of this detail of the K8/K10 processors.

Re: The Three Ways of XOR

#69
post #62

Earlier quoted context omitted.

The way I wrote this was rubbish, edited. I was trying to get at if you have 1110, 1111 then 1110 + 1111 will overflow, but you could xor them.

1110 + 1111 = 1101 1101 - 1110 = 1111 1101 - 1111 = 1110 Overflow doesn't matter if you just want to swap.

That's a really smart observation I hadn't made, thanks for explaining!
Post reply on HN