.Max() vs OrderByDescending().First()

半腔热情 提交于 2020-01-12 14:03:27

问题


This is purely for my own knowledge, if I were going to write the code I would just use .Max().

At first thought .Max() only has to do a single pass through numbers to find the max, while the second way has to sort the entire thing enumerable then find the first one. So it's O(n) vs O(n lg n). But then I was thinking maybe it knows it only needs the highest and just grabs it.

Question: Is LINQ and/or the compiler smart enough to figure out that it doesn't need to sort the entire enumerable and boils the code down to essentially the same as .Max()? Is there a quantifiable way to find out?

IEnumerable<int> numbers = Enumerable.Range(1, 1000);

int max  = numbers.Max();
int max2 = numbers.OrderByDescending(x => x).First();

回答1:


If you are talking about straight LINQ to Objects, then no, it doesn't optimize for that.

Presumably another LINQ provider could do, but that's up to the particulars of the implementation.

For Enumerable, the implementations that Reflector gives me are:

public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
    return new OrderedEnumerable<TSource, TKey>(source, keySelector, null, false);
}

and for First()

public static TSource First<TSource>(this IEnumerable<TSource> source)
{
    if (source == null)
    {
        throw Error.ArgumentNull("source");
    }
    IList<TSource> list = source as IList<TSource>;
    if (list != null)
    {
        if (list.Count > 0)
        {
            return list[0];
        }
    }
    else
    {
        using (IEnumerator<TSource> enumerator = source.GetEnumerator())
        {
            if (enumerator.MoveNext())
            {
                return enumerator.Current;
            }
        }
    }
    throw Error.NoElements();
}



回答2:


Is LINQ and/or the compiler smart enough to figure out that it doesn't need to sort the entire enumerable and boils the code down to essentially the same as .Max()?

No.

Is there a quantifiable way to find out?

A simple benchmark with Stopwatch:

    var numbers = Enumerable.Range(1, 10000000);
    var sw = Stopwatch.StartNew();
    int max = numbers.Max();
    Console.WriteLine(sw.ElapsedMilliseconds);
    sw.Restart();
    int max2 = numbers.OrderByDescending(x => x).First();
    Console.WriteLine(sw.ElapsedMilliseconds);

Max() : 70ms

OrderBy() : 2066ms

Also, OrderBy() fails with an OutOfMemoryException if you increase the count too much beyond that, Max() doesn't.




回答3:


.Max() is O(n), while your OrderByDescending solution isn't - depending on the sort, it's probably O(nlog(n)).

I obviously haven't dug inside the compiler to know, but what you're asking for (an optimization that realizes sort then grab only one item is the same as .max) is rather a lot from a compiler.




回答4:


It appears that OrderedEnumerable is now smart enough to figure out that it doesn't need to sort the list for First and Last().

Note the code at around line 223, TryGetFirst() https://github.com/dotnet/corefx/blob/ed0ee133ac49cee86f10ca4692b1d72e337bc012/src/System.Linq/src/System/Linq/OrderedEnumerable.cs



来源:https://stackoverflow.com/questions/10290986/max-vs-orderbydescending-first

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!