Friday 20 January 2012

C ternary operator hack

Here is a simple bit of C that sets either x or y to value v depending on the value of c..

if (c)   
    x = v;  
else  
    y = v;  

..but why not "improve" this by using the C ternary operator ? : as follows:

 *(c ? &x : &y) = v;  

Now, how does this shape up when compiled on an x86 with gcc -O2 ?  Well, the first example compiles down to a test and a branch where as the second example uses a conditional move instruction (cmove) and avoids the test and branch and is faster code.  Result!

OK, so this isn't rocket science, but does show that a little bit of abuse of the ternary operator can save me a few cycles if the compiler is clued up to use cmove.

17 comments:

  1. This is just awful. Make the compiler smarter and leave this code alone.

    ReplyDelete
  2. This is nice, but I'm often hesitant to design around GCC's heuristics.

    ReplyDelete
  3. I'm not going to advocate the use of it. It just was a small C mind doodle really.

    ReplyDelete
  4. In C++ one can do this directly:

    ( c ? x : y) = v;

    ReplyDelete
    Replies
    1. And the resulting assembler is just like the if statement using g++ -O2

      Delete
  5. But doesn't the hack version force the compiler to use actual memory for x and y, where the normal version would allow it to use registers for those?

    Is the hack version *really* faster in a real-world-ish test case (where x and y aren't declared as volatile or otherwise forced to land in memory locations?)

    ReplyDelete
    Replies
    1. Good observation Kamal. Not sure. I suspect I need to really see how it works in real world examples. We can learn from this is never make rash statements that one saves cycles until one has used the code in real examples rather than simple tests.

      Delete
  6. Nice. I might have to add this to my list of evil C interview questions. ;-)

    ReplyDelete
    Replies
    1. In which case, perhaps you should also consider: http://smackerelofopinion.blogspot.com/2009/07/abusing-c-for-fun.html

      Delete
  7. How about just:
    c ? x = v : y = v;

    ReplyDelete
    Replies
    1. c ? x = v : y = v; produces "error: lvalue required as left operand of assignment", so I suggest using instead the following:

      c ? (x = v) : (y = v);

      or even the more uglier:

      c ? 1, x = y : 1, y = v;

      These compile down to a jump and no cmove.

      Delete
  8. I'm sure there are many other things to worry about before you care about optimizing this use-case.

    Personally I'm all for making the code easily readable. But then I prefer C++ to C.

    ReplyDelete
    Replies
    1. I agree, I don't want to promote bad code style to save a cycle or two. I thought it was an interesting abuse of C really.

      Delete
  9. This works fine in other architectures and compiler?

    ReplyDelete
  10. Linus Torvalds posted an interesting discussion on the CMOVE instruction here. He appears to be deprecating its use:

    http://yarchive.net/comp/linux/cmov.html

    ReplyDelete
    Replies
    1. I wonder how relevant that is to newer Intel CPUs? I need to investigate that.

      Delete