开发者

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

开发者 https://www.devze.com 2023-03-02 19:52 出处:网络
How can this code: var check = 0; for (var numerator = 0; numerator <= maxNumerator; numerator++) { check += numerator >= 0

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;

T开发者_JS百科he 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?


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.


How are you measuring? Your description of the assembly language doesn't sound like something that would make a huge performance difference.

Anyway, if it's really a lot slower, I doubt anyone outside the JIT team can say exactly why this is happening. I have noticed when doing .NET microbenchmarks that seemingly trivial code changes can make the code run significantly faster or slower. If you can make that code (that triggers slowness) as simple as possible, you could complain to MS about it on Microsoft Connect.

You could try copying qdi.Multiplier, qdi.Shift and qdi.Number (whatever they are) to local variables, that sometimes helps.

0

精彩评论

暂无评论...
验证码 换一张
取 消