Does comparing to Math.Min or Math.Max short-circuit?
Asked Answered
E

5

12

When comparing to a minimum or maximum of two numbers/functions, does C# short-circuit if the case is true for the first one and would imply truth for the second? Specific examples of these cases are

if(x < Math.Max(y, z()))

and

if(x > Math.Min(y, z()))

Since Math.Max(y, z()) will return a value at least as large as y, if x < y then there is no need to evaluate z(), which could take a while. Similar situation with Math.Min.

I realize that these could both be rewritten along the lines of

if(x < y || x < z())

in order to short-circuit, but I think it's more clear what the comparison is without rewriting. Does this short-circuit?

Elaterium answered 18/1, 2012 at 19:8 Comment(3)
Assume you call if( x > XYZ(y,z())) How can compiler know the result of XYZ ? Max,Min,Average or anything else?Loanloanda
@Loanloanda Great point, I had not considered this in that way.Elaterium
Also, the "short-circuited" expression may not be equivalent, depending on how Min and Max handle NaN.Gader
S
18

As others have pointed out, the compiler knows nothing about the semantics of Min or Max that would allow it to break the rule that arguments are evaluated before the method is called.

If you wanted to write your own, you could do so easily enough:

static bool LazyLessThan(int x, int y, Func<int> z)
{
    return x < y || x < z();
}

and then call it

if (LazyLessThan(x, y, z))

or

if (LazyLessThan(x, y, ()=>z()))

Or for that matter:

static bool LazyRelation<T>(T x, T y, Func<T> z, Func<T, T, bool> relation)
{
    return relation(x, y) || relation(x, z());
}
...
if (LazyRelation(x, y, ()=>z, (a,b)=> a < b))) 
Septillion answered 18/1, 2012 at 19:27 Comment(3)
That last example would have been great, if it were possible to add a default argument, like so: static bool LazyRelation<T>(T x, T y, Func<T> x, Func<T, T, bool> relation = (a, b)=>a < b)Salome
+1 Great answer, but let's be honest - is all of that more readable / maintainable than if(x < y || x < z()) which the OP suggested himself?Bushtit
I'd say it's less readable, because it replaces a basic idiom with a function whose definition you have to read in order to understand the code.Beauchamp
P
10

No, it doesn't short circuit and z() will always be evaluated. If you want the short circuiting behavior you should rewrite as you have done.

Pep answered 18/1, 2012 at 19:13 Comment(1)
The reason for this is because the runtime has no way of knowing what the effects of Max() and Min() are. You do, which allows you to rewrite in a more performant way.Bushtit
B
5

Math.Min() and Math.Max() are methods just like any other. They have to be evaluated in order to return the value which will be used as the second argument in the comparison. If you want short-circuiting then you will have to write the condition using the || operator as you have demonstrated.

Bina answered 18/1, 2012 at 19:14 Comment(0)
B
3

(Nothing particularly new to add, but I figured I'd share the results of a test I ran on it.)

Math.Max() could easily be inlined by the CLR's just-in-time compiler and from there I was curious whether it might further optimize the code in such a way that it is short-circuited.

So I whipped up a microbenchmark that evaluates the two expressions 1,000,000 times each. For z(), I used a function that calculates Fib(15) using the recursive method. Here are the results of running the two:

x < Math.Max(y, z()) :   8097 ms
x < y || x < z()     :     29 ms

I'm guessing the CLR won't transform the code in any way that prevents method calls from executing, because it doesn't know (and doesn't check to see if) the routine has any side effects.

Beauchamp answered 18/1, 2012 at 20:34 Comment(0)
R
2

No, it doesnt short circuit, at least at the C# compiler level. Math.Min or Math.Max are two ordinary static method calls and the compiler will not optimize in that sense.

The order of evaluation of the code will be: z(), Math.Max, x > ...

If you really want to make sure, check out the IL code.

Religieuse answered 18/1, 2012 at 19:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.