C# IL code optimization: conditional operator (?:) and re-assignment of same variable
Asked Answered
D

1

11

I was reading C# 7.0 changelog and ran into an example that shows new tuples syntax.

private static (int Max, int Min) Range(IEnumerable<int> numbers)
{
    int min = int.MaxValue;
    int max = int.MinValue;
    foreach(var n in numbers)
    {
        min = (n < min) ? n : min;
        max = (n > max) ? n : max;
    }
    return (max, min);
}

And I got curious if the compiler optimizes lines like min = (n < min) ? n : min; cause min = min operation seems a bit useless. I compiled the code (in release mode) and opened it in ILDASM and saw that min = min assignment was still there.

Is it a tough question for the compiler to skip the assignment? Or maybe it's because of some multi-threading issue?

Disguise answered 12/7, 2018 at 5:28 Comment(7)
It was release one.Rectory
C# let JIT compiler to be clever: sharplab.io/…Hileman
@PetSerAl Your example is incomplete because you are using "constants" (the two variables aren't written)... This one is better because it uses parameters.Necrose
@Necrose Your example is incomplete because you are using "constants" That exactly was my intention. Regardless of use of "constants" C# compiler does not optimize resulting IL code: sharplab.io/…Hileman
Following up with @PetSerAl's comment, here is a sharplab that takes in variables (it appears it re-used the value already loaded .. what if these were fields?) -- sharplab.io/…Glaab
For fields it looks like it re-accesses: sharplab.io/… This means at it didn't optimize out completely per "clever programmer", but .. I'm not very good at the machine "ASM" stuff :}Glaab
The same (re-accesses) for [ref parameters]( sharplab.io/…)Necrose
G
5

The way that the conditional operator works is that you always get a value assigned, since the compiler will always expect a value after the '='. Of course the compiler could be written to check whether the left and right hand side is the same, rewriting the variable (right to left) is faster most of the times than using a check to compare the two variables, when taking into account that in most cases a min = min scenario is unlikely and this would only result in an extra check and slow down execution 99.9% of the time.

It's the job of the programmer to determine when to use a conditional operator or a simple if

int min = int.MaxValue;
int max = int.MinValue;
foreach(var n in numbers)
{

    if(n < min) min = n;
    if(n > max) max = n;
}

This way the min = min assignment can be avoided for such circumstances.

Gualterio answered 12/7, 2018 at 5:49 Comment(5)
I agree, however the extra check if the assignment could be dropped would be done once by the compiler, not during execution, you seem to mix this up a bit.Melodize
Wrt./expanding previous comment: assuming that no C# language rules are broken, the compiler could infer the same shortcut as a clever programmer when generating IL. It doesn't. Either this means that rules would be broken, or perhaps that such an optimization is not-worth-the-time-to-implement at this layer so nobody bothered to have Roslyn (or previous) perform the optimization. There is no extra run-time associated if the optimization (same as the clever coder implementation shown) can be done without violating other constraints.Glaab
(It's an interesting question/answer if the answer is because such an optimization would violate some C# [or even CLR] language rule. Otherwise it is mostly just a "well, shucks".)Glaab
Of course, things get "not trivially replaceable" when, given x = q ? z : x, where x refers to a l-value that is not a local variable (ie. perhaps a field, property).Glaab
I agree with KekuSemau. It's a compile-time job. No need to check it in run-time. Maybe this kind of optimisation can break something as @Glaab suggested.Rectory

© 2022 - 2024 — McMap. All rights reserved.