Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can the first of these two code snippets run 3x faster than the second when its doing more work?

How can this code:

var check = 0;

for (var numerator = 0; numerator <= maxNumerator; numerator++)
{
    check += numerator >= 0
           ? numerator - (int) ((numerator * qdi.Multiplier) >> qdi.Shift) * qdi.Number
           : numerator - (int) -((-numerator * qdi.Multiplier) >> qdi.Shift) * qdi.Number;
}

return check;

run 3x faster than this code:

var check = 0;

for (var numerator = 0; numerator <= maxNumerator; numerator++)
{
    check += numerator >= 0
           ? (int) ((numerator * qdi.Multiplier) >> qdi.Shift)
           : (int) -((-numerator * qdi.Multiplier) >> qdi.Shift);
}

return check;

The first code snippet does exactly the same fast divide operation (thats the multiply then shift right) but also a subtraction and multiplication but but the JIT compiler appears to be producing slower code.

I have the disassembly code for each available.
The slower code pushes the rbx register and subtracts 10h from rsp at the start and then adds it back and pops rbx at the end whereas the faster codes doesn't.
The slower code also uses the r11 register for most things where the faster code uses rdx.

Any ideas?

like image 269
Simon Hewitt Avatar asked May 05 '11 10:05

Simon Hewitt


1 Answers

It would appear that the condition used in a ternary operation can affect the code generated.

It would also appear that a ternary optionation can generate less efficient code than a simple if/else.

So changing the loop code in the second snippest to:

if (numerator >= 0) check += (int) ((numerator * qdi.Multiplier) >> qdi.Shift);
else check += (int) -((-numerator * qdi.Multiplier) >> qdi.Shift);

or:

if (numerator < 0) check += (int) -((-numerator * qdi.Multiplier) >> qdi.Shift);
else check += (int) ((numerator * qdi.Multiplier) >> qdi.Shift);

or:

check += numerator < 0
    ? (int) -((-numerator * qdi.Multiplier) >> qdi.Shift)
    : (int) ((numerator * qdi.Multiplier) >> qdi.Shift);

will produce the faster running code.

Actually I find it a bit disturbing that three out of four combinations produce fast code but the other can produce slow code... sometimes.

like image 186
Simon Hewitt Avatar answered Oct 15 '22 00:10

Simon Hewitt